将 IOCP 与多个侦听器一起使用
Using IOCP with multiple listeners
如何为多个侦听器(在不同端口上)设置 IOCP 套接字?我在网上找到的每个示例都只是一个服务器 - 多个客户端示例,我不明白我是否应该创建 multiple IOCP,或者只使用 one 以某种方式为所有听众?
例如,我发现 this example on GitHub,这似乎是对 Win 7 SDK 中的一些代码的轻微修改,这段代码为单个侦听器套接字创建了一个完成端口:
g_hIOCP = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
for( DWORD dwCPU = 0; dwCPU < g_dwThreadCount; dwCPU++ )
{
...
// associate worker thread with this iocp
hThread = CreateThread(NULL, 0, WorkerThread, g_hIOCP, 0, &dwThreadId);
...
}
如何将同一个线程池用于多个完成端口?或者我是否只需要一个用于所有侦听器套接字的完成端口?
您只需要 一个 完成端口即可用于所有侦听器套接字。和几个将在此完成端口上侦听的线程(线程池)。也可以不直接自己创建完成端口和线程池,而是将此任务委托给系统(ntdll)。这可以通过使用 BindIoCompletionCallback
or CreateThreadpoolIo
来完成
所以我们有 - 单个完成端口、多个侦听此端口的工作线程(通常的线程数 == 处理器内核数)和通过 BindIoCompletionCallback
or CreateThreadpoolIo
与此完成端口关联的多个文件(套接字)或CreateIOCompletionPort
(丑陋的逻辑 - 与内部使用的 ZwSetInformationFile(..FileCompletionInformation)
比较)
初始化服务器系统时,为每个必须拥有它的端口发出 AcceptEx() 调用。以'usual'方式使用扩展的OVERLAPPED结构,将监听和客户端套接字都转移到处理IO完成事件的线程。
处理程序线程在执行时可以访问所有每个侦听器和每个客户端的数据,因此可以采取适当的操作。请仔细注意,一个或两个数据项可能需要一个锁定 and/or 队列,以防止多个池线程修改数据,例如,由于两个客户端连接在 'same time'.
每个侦听器不需要完成 port/pool,一个池就可以了。
处理程序线程还应该为下一个要连接的客户端发出另一个 AcceptEx()。
此线程上的其他答案完全合法,但我相信我可以添加一些内容。
让我们讨论一下我理解的完成端口是什么。
完成端口是一个事件接收器。也就是说,一个事件发生,一个完成端口得到通知。
现在,当然有大量的事件分类。完成端口非常适合特定类型的事件:IO 完成。 Cutler 和 gang 有一项专利是用令人惊讶的人类可读语言编写的,它描述了 IOCP 如何与 NT 内核集成。
回顾:完成端口是一个事件接收器,非常适合接收 IO 完成的通知。
现在让我们回到OP的问题:
How do I setup IOCP sockets for multiple listeners (on different ports)?
您的事件是先前发出的 AcceptEx() 调用的完成。它通常是客户端的 connect() 调用,但也可能是某人禁用了您的 NIC。
您首先决定是否要同时或串行处理同一端口上的连接。您还需要决定是否要在不同端口上进行连接以同时或串行处理。
我看到三种可能的方法:
- 所有连接完成都被序列化
预先创建 IOCompletionPort 一次,为每个侦听器 BindIOCompletionCallback,一个工作线程调用 GetQueuedCompletionStatus,如 RbMm 所述
- 同一侦听器(在同一端口上)的连接完成是序列化的,但不同端口上的连接完成是并发的
您必须为每个侦听器创建一个完成端口并启动一个将 GetQueuedCompletionStatus()
的工作程序
- 所有连接完成都是并发的
你可以有一个完成端口,但你启动了多个线程来调用 GetQueuedCompletionStatus()
你不一定像我上面描述的那样限制拥有东西。完成端口很聪明,因此您可以使用一个端口和多个等待程序,然后等待程序获取每个侦听器的锁以进行序列化。内核中的完成端口机制会发现您阻塞了并在适当的时候释放其他等待者。
回顾:首先,您要决定并发程度。然后你选择你需要多少个完成端口。
如何为多个侦听器(在不同端口上)设置 IOCP 套接字?我在网上找到的每个示例都只是一个服务器 - 多个客户端示例,我不明白我是否应该创建 multiple IOCP,或者只使用 one 以某种方式为所有听众?
例如,我发现 this example on GitHub,这似乎是对 Win 7 SDK 中的一些代码的轻微修改,这段代码为单个侦听器套接字创建了一个完成端口:
g_hIOCP = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
for( DWORD dwCPU = 0; dwCPU < g_dwThreadCount; dwCPU++ )
{
...
// associate worker thread with this iocp
hThread = CreateThread(NULL, 0, WorkerThread, g_hIOCP, 0, &dwThreadId);
...
}
如何将同一个线程池用于多个完成端口?或者我是否只需要一个用于所有侦听器套接字的完成端口?
您只需要 一个 完成端口即可用于所有侦听器套接字。和几个将在此完成端口上侦听的线程(线程池)。也可以不直接自己创建完成端口和线程池,而是将此任务委托给系统(ntdll)。这可以通过使用 BindIoCompletionCallback
or CreateThreadpoolIo
所以我们有 - 单个完成端口、多个侦听此端口的工作线程(通常的线程数 == 处理器内核数)和通过 BindIoCompletionCallback
or CreateThreadpoolIo
与此完成端口关联的多个文件(套接字)或CreateIOCompletionPort
(丑陋的逻辑 - 与内部使用的 ZwSetInformationFile(..FileCompletionInformation)
比较)
初始化服务器系统时,为每个必须拥有它的端口发出 AcceptEx() 调用。以'usual'方式使用扩展的OVERLAPPED结构,将监听和客户端套接字都转移到处理IO完成事件的线程。
处理程序线程在执行时可以访问所有每个侦听器和每个客户端的数据,因此可以采取适当的操作。请仔细注意,一个或两个数据项可能需要一个锁定 and/or 队列,以防止多个池线程修改数据,例如,由于两个客户端连接在 'same time'.
每个侦听器不需要完成 port/pool,一个池就可以了。
处理程序线程还应该为下一个要连接的客户端发出另一个 AcceptEx()。
此线程上的其他答案完全合法,但我相信我可以添加一些内容。
让我们讨论一下我理解的完成端口是什么。
完成端口是一个事件接收器。也就是说,一个事件发生,一个完成端口得到通知。
现在,当然有大量的事件分类。完成端口非常适合特定类型的事件:IO 完成。 Cutler 和 gang 有一项专利是用令人惊讶的人类可读语言编写的,它描述了 IOCP 如何与 NT 内核集成。
回顾:完成端口是一个事件接收器,非常适合接收 IO 完成的通知。
现在让我们回到OP的问题:
How do I setup IOCP sockets for multiple listeners (on different ports)?
您的事件是先前发出的 AcceptEx() 调用的完成。它通常是客户端的 connect() 调用,但也可能是某人禁用了您的 NIC。
您首先决定是否要同时或串行处理同一端口上的连接。您还需要决定是否要在不同端口上进行连接以同时或串行处理。
我看到三种可能的方法:
- 所有连接完成都被序列化
预先创建 IOCompletionPort 一次,为每个侦听器 BindIOCompletionCallback,一个工作线程调用 GetQueuedCompletionStatus,如 RbMm 所述
- 同一侦听器(在同一端口上)的连接完成是序列化的,但不同端口上的连接完成是并发的
您必须为每个侦听器创建一个完成端口并启动一个将 GetQueuedCompletionStatus()
的工作程序- 所有连接完成都是并发的
你可以有一个完成端口,但你启动了多个线程来调用 GetQueuedCompletionStatus()
你不一定像我上面描述的那样限制拥有东西。完成端口很聪明,因此您可以使用一个端口和多个等待程序,然后等待程序获取每个侦听器的锁以进行序列化。内核中的完成端口机制会发现您阻塞了并在适当的时候释放其他等待者。
回顾:首先,您要决定并发程度。然后你选择你需要多少个完成端口。