1

我在 IOCP 服务器上工作(重叠 I/O、4 个线程CreateIoCompletionPort、、GetQueuedCompletionStatus等等WSASend)。我还为每个异步 I/O 操作创建了一个自动重置事件并将句柄放在 OVERLAPPED 结构中。

问题是:如何正确发送缓冲区所有连接的套接字?每个套接字都存储在上下文信息结构的链表中。

我不确定下面的方法是否可以?

...
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将不胜感激。
谢谢

4

2 回答 2

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 时删除/重新池化缓冲区。

我认为这应该有效,(?)。

于 2013-11-04T05:23:30.320 回答
2

正如 Martin 所说,这将表现得非常糟糕,并且可能会破坏任何使用您在发送到所有连接期间锁定的套接字列表的任何东西的性能。您没有说这是 UDP 还是 TCP,但如果是 TCP,请注意您现在正在将服务器性能的控制权交给客户端,因为慢速客户端连接上的 TCP 流控制可能会导致写入完成延迟(请参阅在这里)-我假设您正在使用写完成来触发事件?

我假设您的实际要求是您希望避免在服务器上复制数据并分配多个缓冲区,每个连接一个缓冲区是由于内存限制或因为您已经分析了内存副本并发现它很昂贵。

我处理这个问题的方法是拥有一个引用计数缓冲区和一个“缓冲区句柄”,它只是一个稍微扩展的重叠结构,它引用你的单个数据缓冲区并提供你需要的 WSABUF。然后,您可以使用唯一的“缓冲区句柄”向每个连接发出“即发即忘”写入,所有这些都引用单个底层缓冲区。一旦所有写入完成,缓冲区上的引用计数就会减少到零并进行清理 - 正如 Martin 所说,最好通过将缓冲区放入池中以供以后重用来实现清理。

注意:我不确定我是否真的理解你想要做什么(所以我最初删除了我的答案),如果我没有关注,请告诉我,我会调整......

于 2013-11-04T09:15:04.257 回答