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

Posted

技术标签:

【中文标题】如何在 .NET 中的线程上传播 tcplistener 传入连接?【英文标题】:How to spread tcplistener incoming connections over threads in .NET? 【发布时间】:2010-09-08 21:54:58 【问题描述】:

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

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

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

【问题讨论】:

【参考方案1】:

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

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 个并发传入连接,所以现在还可以。 Think listener 和 tcpListener 在示例中混淆了,否则代码很好。找到了这个:msdn.microsoft.com/en-us/library/… 根据我在这里找到的。 不需要那个“connectionWaitHandle”!只需在EndAccept 之后立即致电BeginAcceptClientprivate void HandleAsyncConnection(IAsyncResult result) var listener = (TcpListener)result.AsyncState; var client = listener.EndAcceptTcpClient(result); listener.BeginAcceptTcpClient(HandleAsyncConnection, listener); ...【参考方案2】:

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

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

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

【讨论】:

【参考方案3】:

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

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

【讨论】:

这个我会记住的,虽然我真的很想立即回答每个传入的连接。感谢您的建议。 嗨! @Dror Helper,您的建议看起来不错。那么如何实施效率策略?可以分享一下吗,有sn-p的代码吗?【参考方案4】:

O'Reilly C# 3.0 Cookbook 中有一个很好的示例。您可以从http://examples.oreilly.com/9780596516109/CSharp3_0CookbookCodeRTM.zip下载随附的源代码

【讨论】:

【参考方案5】:

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

抱歉,没有样品。

【讨论】:

以上是关于如何在 .NET 中的线程上传播 tcplistener 传入连接?的主要内容,如果未能解决你的问题,请参考以下文章

利用 ASP.NET Core 中的标头传播实现分布式链路跟踪

利用 ASP.NET Core 中的标头传播实现分布式链路跟踪

如何避免从工作线程到 GUI 线程的主体传播

处理业务:事件是如何在 pipeline 中传播的

如何在 C# .net 网页上显示来自另一个线程的弹出窗口?

线程(任务)中的异常不会像示例所暗示的那样传播