[英]Concurrent Clients issue in C# Sockets
我正在開發客戶端服務器應用程序,Windows Server和Linux Client。 我正在用多個並發客戶端測試服務器。 我僅嘗試了來自客戶端的20個並發連接,但我注意到盡管所有20個請求都是相同的,但仍未處理某些請求。 他們進入隊列,由於某種原因,輪到客戶端時關閉了客戶端(客戶端連接超時為5秒)。
然后,我添加了一個Thread.Sleep(1000),以檢查它是否真的是異步的,但后來我意識到,直到超時,它才處理其他請求。 盡管事實
現在我想知道我在這里缺少什么,因為大多數並發連接會發生這種情況?
public static void StartServer(IPAddress ipAddr, int port)
{
//IPEndPoint serverEndPoint = new IPEndPoint(ipAddr, port);
IPEndPoint serverEndPoint = new IPEndPoint(IPAddress.Any, port);
Socket clientListener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
try
{
clientListener.Bind(serverEndPoint);
clientListener.Listen(500);
Console.WriteLine("-- Server Listening: {0}:{1}",ipAddr,port);
while (true)
{
resetEvent.Reset();
Console.WriteLine("|| Waiting for connection");
clientListener.BeginAccept(new AsyncCallback(AcceptConnection), clientListener);
resetEvent.WaitOne();
}
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
public static void AcceptConnection(IAsyncResult ar)
{
// Get the socket that handles the client request.
Socket listener = (Socket)ar.AsyncState;
Socket handler = listener.EndAccept(ar);
// Signal the main thread to continue.
resetEvent.Set();
// Create the state object.
JSStateObject state = new JSStateObject();
state.workSocket = handler;
if (handler.Connected)
{
Console.WriteLine("** Connected to: {0}", handler.RemoteEndPoint.ToString());
state.workingDirectory = JSUtilityClass.CreatetTemporaryDirectry();
try
{
Thread.Sleep(1000);
Receive(state);
}
catch (Exception e)
{
handler.Shutdown(SocketShutdown.Both);
handler.Close();
Console.WriteLine(e.Message);
}
}
}
我創建了一個測試,該測試發送了100次連接嘗試,發現一些使速度變慢的事情。
我在AcceptConnection中放置一個斷點以查看調用堆棧,就是這樣
ConsoleApplication1.exe!ConsoleApplication1.Program.AcceptConnection(System.IAsyncResult ar) Line 62 C#
System.dll!System.Net.LazyAsyncResult.Complete(System.IntPtr userToken) + 0x69 bytes
System.dll!System.Net.ContextAwareResult.CaptureOrComplete(ref System.Threading.ExecutionContext cachedContext, bool returnContext) + 0xab bytes
System.dll!System.Net.ContextAwareResult.FinishPostingAsyncOp(ref System.Net.CallbackClosure closure) + 0x3c bytes
System.dll!System.Net.Sockets.Socket.BeginAccept(System.AsyncCallback callback, object state) + 0xe3 bytes
ConsoleApplication1.exe!ConsoleApplication1.Program.StartServer(System.Net.IPAddress ipAddr, int port) Line 48 + 0x32 bytes C#
因此,回調AcceptConnection
從與調用BeginAccept相同的線程運行。 我看了看帶有反射器的FinishPostingAsyncOp
,它使用的是異步模式,如果隊列中已經有一個套接字操作等待處理,它將在當前線程上執行,否則,如果沒有任何待處理,它將稍后將在其他線程中處理
SocketAsyncEventArgs sae = new SocketAsyncEventArgs();
sae.Completed += new EventHandler<SocketAsyncEventArgs>(SocketOperation_Completed);
if (!clientListener.AcceptAsync(sae))
AcceptConnection(clientListener, sae); // operation completed synchronously, process the result
else
// operation will complete on a IO completion port (different thread) which we'll handle in the Completed event
因此,如您所見,在這種情況下該程序實際上是完全同步的,並且使用1秒的Thread.Sleep至少需要100秒才能接受所有連接,到那時大多數連接將超時。
即使BeginAccept方法摘要說
開始異步操作以接受傳入的連接嘗試。
事實證明,故事還有更多
從MSDN http://msdn.microsoft.com/en-AU/library/system.net.sockets.socket.beginaccept.aspx
BeginAccept(Int32,AsyncCallback,Object)開始異步操作以接受傳入的連接嘗試,並接收客戶端應用程序發送的第一個數據塊。
因此,它會在觸發回調之前以較短的超時執行讀取操作。 您可以通過指定receiveSize
為0來禁用它。
clientListener.BeginAccept(new AsyncCallback(AcceptConnection), clientListener);
至
clientListener.BeginAccept(0, new AsyncCallback(AcceptConnection), clientListener);
這樣可以加快速度,如果我們從AcceptConnection
刪除Thread.Sleep(1000)
,那么所有連接都將很快被接受。
如果您將Thread.Sleep(1000)
留在其中以模擬工作負載或僅用於測試,則您可能希望通過執行以下操作准備服務器以處理此類負載
int minWorkerThreads = 0;
int minCompletionPortThreads = 0;
ThreadPool.GetMinThreads(out minWorkerThreads, out minCompletionPortThreads);
ThreadPool.SetMinThreads(minWorkerThreads, 100);
其中100是您希望可用於處理套接字操作的線程數量。
另一件事,這是個人喜好問題,只是想知道您可能想從AcceptConnection內調用BeginAccept
,因此不需要while循環。 即改變這個
while (true)
{
resetEvent.Reset();
Console.WriteLine("|| Waiting for connection");
clientListener.BeginAccept(new AsyncCallback(AcceptConnection), clientListener);
resetEvent.WaitOne();
}
對此
Console.WriteLine("|| Waiting for connection");
clientListener.BeginAccept(new AsyncCallback(AcceptConnection), clientListener);
並將另一個BeginAccept
放在AcceptConnection
public static void AcceptConnection(IAsyncResult ar)
{
// Get the socket that handles the client request.
Socket listener = (Socket)ar.AsyncState;
// start another listening operation
listener.BeginAccept(new AsyncCallback(AcceptConnection), listener);
... the rest of the method
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.