是否可以为多个客户端创建单个套接字、单个线程、TCP/IP 数据包reader?

Is it possible to create a single socket, single thread, TCP/IP packet reader for multiple clients?

典型的服务器应用程序会为每个传入连接创建一个套接字,从而生成一个新线程。

但是,是否可以在单个线程中自己进行多路分解?我想要的是应用程序为每个端点保留一个状态机,当数据在单个线程中可用时反序列化消息。

线程是最简单的,我有一个可行的实现。然而,每个线程都会相当快地增加开销,并且挥之不去的连接将需要资源。我想要的是,状态机为每个连接建立一条消息(无论如何,线程代码已经这样做了),并在完成后将反序列化的消息分派到工作队列。

这在 .NET 中可行吗? P/Invoke 也是一个可以接受的解决方案。

A typical server application would create a socket for each incoming connection spawn a new thread.

这不是真的说法。很少有服务器——只有那些不需要扩展到大量连接客户端的服务器,甚至不是所有服务器——会将整个线程专用于单个连接。

我会说只有最基本的服务器才会使用每个连接一个线程的设计。我想由于实验服务器(即爱好者学习编写网络代码)比生产服务器多得多,所以绝对数量可能在每个连接的线程数方面。

但是恕我直言,只有生产服务器才真正与这个问题相关,因为它们展示了好的设计和实现。对于那些,每个连接线程肯定会占少数。

However is it possible to do the demuxing yourself in a single thread?

使用 Socket.Select() 可以有一个线程专用于处理多个套接字。

然而,更典型的是使用可用于套接字的几个异步编程 API 之一,它使用 I/O 完成端口来处理 I/O操作到专用于此目的的线程池。这允许并发但有效地使用线程以最小化上下文切换。

我对当前 .NET 4.5 和 C# 5.0 功能的首选方法是将套接字包装在 NetworkStream 的实例中,这样我就可以使用 ReadAsync()WriteAsync()方法,这又允许在 C# 代码中使用 await。这使得异步代码更易于阅读和实现。

但是你可以使用例如Socket.BeginReceive()Socket.ReceiveAsync()(后者对于需要极高可扩展性的服务器很有用……尽管前者仍然比每个连接的线程更具可扩展性)。

无论使用 API,典型的服务器实现都会为每个连接包含一个状态对象 class。这可以通过 thread-per-connection 设计来完成,就像使用 async I/O 设计一样容易。您的状态机将驻留在该状态对象 class 中,以便处理套接字上的 I/O 操作可以使用状态机。

当然,诀窍是使用异步 IO(即 BeginAccept、EndAccept、BeginRead、EndRead、BeginWrite 和 EndWrite)。这使您无需线程即可处理多个客户端。这就是 React 的工作方式。

使用Socket.Select方法。

当您调用该方法时,您将三个套接字列表传递给它。一个是等待读取的套接字列表,一个是等待写入的列表(如果写入缓冲区已满,您将不得不等待写入),一个是等待错误的列表.您还告诉它等待的最长时间,它会阻塞那么长时间,直到其中一个套接字准备就绪。当函数 returns 时,它会修改列表以删除未就绪的套接字。

当您创建读取数组时,将 master/mother 套接字放入,以便您知道何时有新连接。还要放入任何现有连接。将所有这些也放在错误数组中。对于write数组,只需要在write buffer满了的时候再放入socket即可。可能这是您在第一次迭代中可以忽略的事情,但当您开始处理大量流量时,它会很重要。

Socket.Select() returns时,您只需要遍历读取列表中剩余的套接字,并根据您的服务器需要处理数据。遍历写入并推出任何剩余的排队数据。遍历错误并关闭那些套接字或处理错误。请记得在再次调用 select 之前将空闲套接字放回列表中!