如何在完成数据包可用时阻止I / O完成端口阻塞?

时间:2014-04-10 01:16:55

标签: winsock2 iocp overlapped-io

我有一个使用Microsoft's I/O Completion Port (IOCP)机制来管理异步网络套接字通信的服务器应用程序。总的来说,这种IOCP方法在我的环境中表现得非常好。但是,我遇到了一个边缘案例,我正在寻求指导:

出于测试目的,我的服务器应用程序通过千兆位局域网将数据(简称~400 KB /秒)传输到单个客户端。一切都很好......直到我从LAN断开客户端的以太网电缆。以这种方式断开电缆可防止服务器立即检测到客户端已消失(即客户端的TCP网络堆栈未向服务器发送连接终止通知)

与此同时,服务器继续对客户端进行WSASend次呼叫......并且由于这些呼叫是异步的,因此它们似乎已成功"成功" (即,数据由OS在套接字的出站队列中缓冲。)

虽然这一切都在发生,但我在GetQueuedCompletionStatus上阻塞了16个线程,等待从端口可用时检索完成数据包。在断开客户端电缆之前,有一个恒定的完成数据包流。现在,一切(如预期的那样)似乎已经停止了......大约32秒。 32秒后,IOCP重新启动,返回FALSE非空lpOverlapped值。 GetLastError返回121(信号量超时时间已过期。)我只能假设错误121是WSASend的工件,在TCP堆栈确定客户端消失后最终超时?

网络堆栈花了32秒才弄清楚我的客户端已经不见了。问题是,当系统做出这个决定时,我的IOCP就瘫痪了。例如,发送到同一IOCP的WSAAccept事件不会被GetQueuedCompletionStatus上阻塞的16个线程中的任何一个处理,直到收到失败的完成数据包(指示错误121)。

我最初的计划是在调用WSAWaitForMultipleEvents后立即使用WSASend。如果套接字事件未在(例如3秒)内发出信号,则我终止套接字连接并继续(为了防止对我的IOCP产生广泛的阻塞影响)。不幸的是,WSAWaitForMultipleEvents似乎永远不会遇到超时(因此异步套接字可能因异步而发出信号?或者将数据复制到TCP队列是否有资格获得信号?)

我仍然试图将这一切排除在外,但希望有人对如何防止IOCP挂起有一些见解。

其他细节:我的服务器应用程序在Win7上运行,有8个内核; IOCP配置为最多使用8个并发线程;我的线程池有16个线程。大量的RAM,处理器和带宽。

提前感谢您的建议和意见。

1 个答案:

答案 0 :(得分:1)

WSASend()完成通常会在这种情况下失效。在TCP堆栈超时重发尝试并完成所有未完成的错误发送之前,您将无法获取它们。这不会阻止任何其他操作。我希望您测试不正确或者代码中有错误。

请注意,您的“修复”存在缺陷。如果发件人的发送速度快于消费者可以使用的速度,则可以在正常连接期间的任何时刻看到此“延迟发送完成”情况。见this article on TCP flow control and async writes。一个更好的计划是使用一个计数器来计算你想要允许的写入量(每个连接),如果达到该计数器则停止发送,然后当它低于“低水位线”阈值时恢复。

请注意,如果您已将网线拔出到机器中,您希望其他任何操作完成?读取只会坐在那里,只有在写入失败后才会失败,而且AcceptEx只会坐在那里等待条件自行纠正。