如何在.NET中将tcplistener传入连接分布到线程上?

15

在使用Net.Sockets.TcpListener时,处理传入连接(.AcceptSocket)的最佳方法是什么?

一种想法是,在接受新传入连接时启动一个新线程,而tcplistener则保持可用以获取更多传入连接(对于每个新传入连接,都会创建一个新线程)。所有与发起连接的客户端的通信和终止将在该线程中处理。

欢迎提供C#或VB.NET示例代码。

5个回答

15

我一直在使用的代码看起来像这样:

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();
  }
}

感谢提供源代码,我会按照这样的方式编写它。新线程可能会很耗费资源,但由于我目前并没有扩展到5或6个并发连接以上,所以现在这样做还是可以的。 - Jorrit Reedijk
1
代码还不错,只是在示例中listener和tcpListener有些混淆了。在这里我找到了http://msdn.microsoft.com/en-us/library/system.net.sockets.tcplistener.beginaccepttcpclient.aspx。 - Justin Wignall
不需要使用 "connectionWaitHandle"!只需在“EndAccept”之后立即调用BeginAcceptClientprivate void HandleAsyncConnection(IAsyncResult result) { var listener = (TcpListener)result.AsyncState; var client = listener.EndAcceptTcpClient(result); listener.BeginAcceptTcpClient(HandleAsyncConnection, listener); ... - gatopeich

3

我相信你可以像在.NET中处理其他异步操作一样来处理它:调用方法的BeginXxx版本,在这种情况下是BeginAcceptSocket。您的回调将在线程池上执行。

与每个连接一个线程相比,池化线程通常具有更好的可伸缩性:一旦连接数超过几十个,系统在在线程之间切换方面会比实际工作更加努力。此外,每个线程都有自己的堆栈,其大小通常为1MB(尽管这取决于链接标志),必须在2GB虚拟地址空间中找到;在实践中,这将限制您的线程数少于1000个。

我不确定.NET的线程池当前是否使用它,但Windows有一个名为I/O Completion Port的内核对象,可帮助扩展I/O。您可以将线程与此对象关联,并且可以将I/O请求(包括接受传入连接)与其关联。当I/O完成时(例如,连接到达),Windows将释放等待的线程,但仅当当前可运行的线程数(未因某些其他原因而被阻止)小于完成端口的配置可伸缩性限制时才会释放。通常,您会将其设置为核心数的小倍数。


2
我建议采用不同的方法: 我的建议仅使用两个线程。 *一个线程检查传入连接。 *当新连接打开时,此信息将写入共享数据结构,该数据结构保存所有当前打开的连接。 *第二个线程枚举该数据结构,并为每个打开的连接接收发送的数据并发送回复。
这种解决方案在线程方面更具可扩展性,如果正确实现,应该比每个打开的连接都要开启一个新线程具有更好的性能。

我会记住这个建议的,虽然我真的很想立即回答每一个传入的连接。谢谢你的建议。 - Jorrit Reedijk
你好!@Dror Helper,你提供的建议看起来很不错。那么如何实现高效策略?你能分享一下吗?你有任何代码片段吗? - bashkan

1

0
我会使用线程池,这样每次都不需要启动新的线程(因为这种方式有点昂贵)。同时,我也不会无限期地等待更多的连接,因为客户端可能不会关闭他们的连接。您计划如何将客户端路由到同一线程?
很抱歉,没有示例。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接