如何在.NET中的线程上传播tcplistener传入连接?

时间:2008-09-15 12:44:48

标签: .net multithreading tcplistener

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

这个想法是在接受新的传入连接时启动一个新线程,然后tcplistener保持可用于进一步的传入连接(并且为每个新的传入连接创建一个新线程)。与发起连接的客户端的所有通信和终止都将在线程中处理。

示例C#VB.NET代码表示赞赏。

5 个答案:

答案 0 :(得分:14)

我一直在使用的代码如下:

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

答案 1 :(得分:3)

我相信你这样做的方式与.NET中的任何其他异步操作相同:你调用方法的BeginXxx版本,在本例中是BeginAcceptSocket。您的回调将在线程池上执行。

池化线程通常比每次连接线程更好地扩展:一旦你获得了几十个连接,系统在线程之间切换比在完成实际工作时更加困难。此外,每个线程都有自己的堆栈,其大小通常为1MB(虽然它取决于链接标志),必须在2GB虚拟地址空间中找到(在32位系统上);实际上,这限制了你不到1000个线程。

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

答案 2 :(得分:2)

我想建议一种不同的方法: 我的建议只使用两个线程。   *一个线程检查是否存在连接。   *当新连接打开时,此信息将写入包含所有当前打开连接的共享数据结构。   *第二个线程枚举数据结构和每个打开的连接接收发送的数据并发送回复。

此解决方案在线程方面更具可扩展性,如果在当前实现,则应该具有更好的性能,然后为每个打开的连接打开一个新线程。

答案 3 :(得分:1)

O'Reilly C#3.0 Cookbook中有一个很好的例子。您可以从http://examples.oreilly.com/9780596516109/CSharp3_0CookbookCodeRTM.zip

下载随附的来源

答案 4 :(得分:0)

我会使用一个线程池,这样你就不必每次都开始一个新的线程(因为这有点贵)。我也不会因为客户关闭他们的连接而无限制地等待连接。您打算如何每次将客户端路由到同一个线程?

抱歉,没有样品。