当完成数据包可用时,如何防止I/O完成端口阻塞



我有一个服务器应用程序,它使用Microsoft的I/O完成端口(IOCP)机制来管理异步网络套接字通信。总的来说,这种IOCP方法在我的环境中表现得非常好。然而,我遇到了一个边缘情况,我正在寻求指导:

为了进行测试,我的服务器应用程序正在通过千兆局域网将数据流传输(比方说大约400 KB/秒)到单个客户端。一切都很好。。。直到我断开客户端的以太网电缆与LAN的连接。以这种方式断开电缆可以防止服务器立即检测到客户端已经消失(即客户端的TCP网络堆栈不会向服务器发送连接终止的通知)

同时,服务器继续对客户端进行WSASend调用。。。由于这些调用是异步的,它们看起来"成功"了(即数据由操作系统缓冲在套接字的出站队列中)。

当这一切发生时,我在GetQueuedCompletionStatus上有16个线程被阻塞,等待从端口检索可用的完成数据包。在断开客户端电缆之前,会有源源不断的完成数据包。现在,一切(如预期的那样)似乎都停止了。。。持续约32秒。32秒后,IOCP将返回具有非空lpOverlapped值的FALSEGetLastError返回121(信号量超时时间已过期。)我只能假设错误121是WSASend在TCP堆栈确定客户端不在后最终超时的产物?

我很满意网络堆栈需要32秒才能发现我的客户端不在了。问题是,当系统做出这个决定时,我的IOCP瘫痪了。例如,发布到同一IOCP的WSAAccept事件不由在GetQueuedCompletionStatus上阻塞的16个线程中的任何线程处理,直到接收到失败的完成数据包(指示错误121)。

我最初的解决方案是在调用WSASend后立即使用WSAWaitForMultipleEvents。如果套接字事件在(例如3秒)内没有发出信号,那么我终止套接字连接并继续(希望防止对我的IOCP产生广泛的阻塞影响)。不幸的是,WSAWaitForMultipleEvents似乎从未遇到超时(所以异步套接字可能是通过异步发出的信号?或者将数据复制到TCP队列符合信号条件?)

我仍在努力解决这一切,但希望有人能对如何防止IOCP挂起有一些见解。

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

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

在这种情况下,WSASend()完成通常会停滞。直到TCP堆栈超时其重新发送尝试并错误地完成所有未完成的发送,您才能得到它们。这不会阻止任何其他操作。我预计你要么测试不正确,要么代码中有错误。

请注意,您的"修复"是有缺陷的。如果发送方的发送速度快于消费者的消费速度,那么在正常连接期间的任何时候都可能出现这种"延迟发送完成"的情况。请参阅这篇关于TCP流控制和异步写入的文章。一个更好的计划是使用一个计数器来计算您想要允许的取消写入量(每个连接),如果达到该计数器,则停止发送,然后在其降至"低水位线"阈值以下时继续发送。

请注意,如果您已将网络电缆插入机器,您希望如何完成任何其他操作?读取只会停留在那里,只有在写入失败后才会失败,AcceptEx只会停留那里,等待条件自行纠正。

最新更新