我在IOCP服务器上工作(重叠I / O,4个线程,CreateIoCompletionPort
,GetQueuedCompletionStatus
,WSASend
等。我还创建了一个自动重置事件,并将句柄放在OVERLAPPED结构中,用于每个异步I / O操作。
问题是:如何正确地发送所有连接套接字的缓冲区?每个套接字都存储在上下文信息结构的链接列表中。
我不确定下面的方法是否合适?
...
DWORD WINAPI WorkerThread() { // 1 of 4 workthread
...
GetQueuedCompletionStatus(...);
...
PPER_SOCKET_CONTEXT pTmp1, pTmp2;
pTmp1 = g_pCtxtList; // start of linked list with sockets
EnterCriticalSection(&g_CriticalSection);
while( pTmp1 )
{
pTmp2 = pTmp1->pCtxtBack;
WaitForSingleObject(pTmp1->pIOContext->Overlapped.hEvent,infinite);
// if there is any pending wsasend on this socket,wait to completed, so
// we can post another wsasend using the same overlapped structure
// and buffer
WSASend(pTmp1->Socket,...,&(pTmp1->pIOContext->Overlapped), NULL);
pTmp1 = pTmp2;
}
LeaveCriticalSection(&g_CriticalSection);
...
}
如果另一个线程也试图同时做同样的工作会发生什么?
使用GQCS并在所有线程中等待功能是不错的主意?
任何关于多线程iocp服务器中所有客户端wsasends
的线索都将受到赞赏
THX
答案 0 :(得分:3)
不确定我理解其中一些。 IOCP通常不在OVL结构中使用hEvent字段。通过将完成消息排队到“完成端口”(即队列)来发信号通知I / O完成。您似乎正在使用hEvent字段来管理单个发送数据缓冲区和OVL块的“异常”额外信号。
显然,我的帖子中没有完整的故事,但在我看来,你在tx方面为自己做了大量的工作,连续发送会扼杀性能:)
您是否必须使用相同的OVL /缓冲区对象进行连续发送?我通常做的是为每个发送使用不同的OVL /缓冲区,并立即排队。内核将按顺序发送缓冲区并为每个缓冲区返回完成消息。套接字上的多个IOCP tx请求没有问题 - 这就是OVL块的用途 - 在内核堆栈中将它们链接在一起。
对于未完成的套接字有多个IOCP接收请求存在问题 - 可能会发生两个池线程同时获得同一套接字的完成数据包,因此可能导致无序处理。解决该问题“正确”需要在每个rx缓冲区/ OVL对象中发出递增序列号,并在每个套接字对象中使用临界区和缓冲区列表来“保存”无序缓冲区直到所有更早的已经处理过了。我怀疑许多IOCP服务器只是在一次只有一个rx IOCP请求时避免了这个问题,(可能是以牺牲性能为代价)。
以这种方式获取大量缓冲区如果它们被不断构建和销毁可能会有些麻烦,所以我通常不会打扰并且只是在启动时创建几千个并推送它们,(好吧,指针)对于他们而言,在生产者 - 消费者'池队列'上,当需要tx或rx时将它们弹出并再次推回它们。在tx的情况下,当其中一个IOCP池线程拾取发送完成消息时,会发生这种情况。在rx的情况下,当池线程(或者某个其他线程使池对象排队的对象)处理它并且不再需要它时,就会发生这种情况。
啊..你想把完全相同的内容发送到套接字列表 - 就像聊天服务器类型一样。
行。那么一个缓冲区和多个OVL块怎么样?我没有尝试过,但不明白为什么它不起作用。在单个缓冲区对象中,保留一个原子引用计数,表明您在“发送到所有客户端”循环中发送的重叠发送请求的数量。当你在完成数据包中得到缓冲区时,将refCount递减为零,并在你降到0时删除/重新填充缓冲区。
我认为应该有用,(?)。
答案 1 :(得分:2)
正如马丁所说,这将会非常糟糕,可能会破坏使用您在发送到所有连接的整个持续时间内锁定的套接字列表的任何内容的性能。您不会说这是UDP还是TCP,但是如果TCP知道您正在将服务器性能的控制权交给客户端作为慢速客户端连接上的TCP流控制可能导致写入完成延迟(参见here) - 我假设您正在使用写入完成来触发事件?
我认为您的实际要求是您希望避免在服务器上复制数据并分配多个缓冲区,每个连接一个由于内存限制或者您已经分析了内存副本并发现它和#39;昂贵。
我处理这个问题的方法是使用一个引用计数缓冲区和一个缓冲区句柄'这只是一个略微扩展的重叠结构,它引用您的单个数据缓冲区并提供您需要的WSABUF。然后你可以发出一声“忘掉”并忘记'使用唯一的“缓冲区句柄”写入每个连接。所有这些都指的是单个底层缓冲区。一旦所有写入完成,缓冲区上的引用计数减少到零并且它会清理 - 正如Martin所说,最好通过将缓冲区放入池中以便以后重用来实现清理。
注意:我不确定我是否真正了解您要做的事情(所以我原定删除了我的答案),如果我没有关注,请告诉我我会调整......