[英]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.