问题:
设计一个高效且非常快速的命名管道客户端服务器框架。
当前状态:
我已经拥有经过实战验证的生产测试框架。它很快,但是每个管道连接使用一个线程,如果有很多客户端,线程数可能很快就会很高。我已经使用了可以根据需要扩展的智能线程池(实际上是任务池)。
我已经对管道使用了 OVERLAPED 模式,但后来我用 WaitForSingleObject 或 WaitForMultipleObjects 阻塞,这就是为什么我需要在服务器端的每个连接一个线程
所需的解决方案:
客户端很好,但在服务器端,我只想为每个客户端请求而不是每个连接使用一个线程。因此,我不会在客户端的整个生命周期(连接/断开连接)中使用一个线程,而是每个任务使用一个线程。因此,仅当客户端请求数据时,仅此而已。
我在 MSDN 上看到一个示例,它使用 OVERLAPED 结构数组,然后使用 WaitForMultipleObjects 等待它们。我觉得这是一个糟糕的设计。我在这里看到两个问题。首先,您必须维护一个可以变得非常大的数组,并且删除将是昂贵的。其次,您有很多事件,每个数组成员都有一个事件。
我还看到了完成端口,例如CreateIoCompletionPort和GetQueuedCompletionStatus,但我看不出它们有什么更好的地方。
我想要的是ReadFileEx和WriteFileEx做的事情,它们在操作完成时调用一个回调例程。这是一种真正的异步编程风格。但问题是 ConnectNamedPipe 不支持它,而且我看到线程需要处于警报状态,您需要调用一些 *Ex 函数来获得它。
那么如何最好地解决这样的问题呢?
以下是 MSDN 的做法:http: //msdn.microsoft.com/en-us/library/windows/desktop/aa365603 (v=vs.85).aspx
我用这种方法看到的问题是,如果WaitForMultipleObjects的限制是 64 个句柄,我看不到如何同时连接 100 个客户端。当然我可以在每次请求后断开管道,但想法是像在 TCP 服务器中一样拥有一个永久的客户端连接,并在整个生命周期中跟踪客户端,每个客户端都有唯一的 ID 和客户端特定的数据。
理想的伪代码应该是这样的:
repeat
// wait for the connection or for one client to send data
Result = ConnectNamedPipe or ReadFile or Disconnect;
case Result of
CONNECTED: CreateNewClient; // we create a new client
DATA: AssignWorkerThread; // here we process client request in a thread
DISCONNECT: CleanupAndDeleteClient // release the client object and data
end;
until Aborted;
这样我们就只有一个监听线程来接受连接/断开/onData 事件。线程池(工作线程)只处理实际请求。这样 5 个工作线程可以为许多连接的客户端提供服务。
PS我当前的代码应该不重要。我在 Delphi 中对此进行了编码,但它是纯 WinAPI,因此语言无关紧要。
编辑:
现在 IOCP 看起来像解决方案:
I/O 完成端口为在多处理器系统上处理多个异步 I/O 请求提供了一个高效的线程模型。当一个进程创建一个 I/O 完成端口时,系统会为请求创建一个关联的队列对象,这些请求的唯一目的是为这些请求提供服务。处理许多并发异步 I/O 请求的进程可以通过将 I/O 完成端口与预先分配的线程池结合使用,而不是通过在接收 I/O 请求时创建线程来更快、更有效地完成此操作。