在使用Net.Sockets.TcpListener时,处理传入连接(.AcceptSocket)的最佳方法是什么?
一种想法是,在接受新传入连接时启动一个新线程,而tcplistener则保持可用以获取更多传入连接(对于每个新传入连接,都会创建一个新线程)。所有与发起连接的客户端的通信和终止将在该线程中处理。
欢迎提供C#或VB.NET示例代码。
在使用Net.Sockets.TcpListener时,处理传入连接(.AcceptSocket)的最佳方法是什么?
一种想法是,在接受新传入连接时启动一个新线程,而tcplistener则保持可用以获取更多传入连接(对于每个新传入连接,都会创建一个新线程)。所有与发起连接的客户端的通信和终止将在该线程中处理。
欢迎提供C#或VB.NET示例代码。
我一直在使用的代码看起来像这样:
class Server
{
private AutoResetEvent connectionWaitHandle = new AutoResetEvent(false);
public void Start()
{
TcpListener listener = new TcpListener(IPAddress.Any, 5555);
listener.Start();
while(true)
{
IAsyncResult result = listener.BeginAcceptTcpClient(HandleAsyncConnection, listener);
connectionWaitHandle.WaitOne(); // Wait until a client has begun handling an event
connectionWaitHandle.Reset(); // Reset wait handle or the loop goes as fast as it can (after first request)
}
}
private void HandleAsyncConnection(IAsyncResult result)
{
TcpListener listener = (TcpListener)result.AsyncState;
TcpClient client = listener.EndAcceptTcpClient(result);
connectionWaitHandle.Set(); //Inform the main thread this connection is now handled
//... Use your TcpClient here
client.Close();
}
}
我相信你可以像在.NET中处理其他异步操作一样来处理它:调用方法的BeginXxx版本,在这种情况下是BeginAcceptSocket。您的回调将在线程池上执行。
与每个连接一个线程相比,池化线程通常具有更好的可伸缩性:一旦连接数超过几十个,系统在在线程之间切换方面会比实际工作更加努力。此外,每个线程都有自己的堆栈,其大小通常为1MB(尽管这取决于链接标志),必须在2GB虚拟地址空间中找到;在实践中,这将限制您的线程数少于1000个。
我不确定.NET的线程池当前是否使用它,但Windows有一个名为I/O Completion Port的内核对象,可帮助扩展I/O。您可以将线程与此对象关联,并且可以将I/O请求(包括接受传入连接)与其关联。当I/O完成时(例如,连接到达),Windows将释放等待的线程,但仅当当前可运行的线程数(未因某些其他原因而被阻止)小于完成端口的配置可伸缩性限制时才会释放。通常,您会将其设置为核心数的小倍数。
在O'Reilly C# 3.0 Cookbook中有一个很好的例子。您可以从http://examples.oreilly.com/9780596516109/CSharp3_0CookbookCodeRTM.zip
下载相应的源代码。
BeginAcceptClient
:private void HandleAsyncConnection(IAsyncResult result) { var listener = (TcpListener)result.AsyncState; var client = listener.EndAcceptTcpClient(result); listener.BeginAcceptTcpClient(HandleAsyncConnection, listener); ...
- gatopeich