closesocket() 未完成 IOCP 的挂起操作

Posted

技术标签:

【中文标题】closesocket() 未完成 IOCP 的挂起操作【英文标题】:closesocket() not completing pending operations of IOCP 【发布时间】:2017-10-12 06:44:22 【问题描述】:

我目前正在使用 C++ 开发服务器应用程序。我的主要灵感来自这些例子:

Windows SDK IOCP Excample

The I/O Completion Port IPv4/IPv6 Server Program Example

我的应用与这些(socketobj、packageobj、...)非常相似。

总的来说,我的应用程序运行正常。唯一仍然给我带来麻烦的是半开连接。

我的策略是:我检查一个时间段内每个连接的客户端并计算一个“空闲计数器”。如果发生一次完成,我会重置此计时器。如果 Idle 计数器过高,我设置一个布尔值来阻止其他线程发布操作,然后调用closesocket()

我的假设是,现在套接字已关闭,待处理的操作将完成(可能不是立即完成,而是在一段时间后完成)。这也是 MSDN 文档描述的行为(提示,第二段)。我需要这个,因为只有在完成所有操作之后才能释放资源。

长话短说:对我来说不是这样。我用我的 testclient 应用程序和一些 cout 和断点调试做了一些测试,发现关闭套接字的挂起操作没有完成(即使在等待 10 分钟之后)。我也已经尝试在closesocket() 之前调用shutdown(),并且都没有返回错误。

我做错了什么?其他人也有遇到同样的状况吗? MSDN 文档有错吗?有哪些替代方案?

我目前正在考虑“逗留”功能,或者使用 CancelIoEx() 函数显式取消每个操作

编辑: (感谢您的回复)

昨天晚上我为每个 sockedobj 添加了一个链表来保存挂起操作的 per io obj。有了这个,我尝试了CancelIOEx() 函数。对于大多数操作,该函数返回 0 和 GetLastError() 返回 ERROR_NOT_FOUND

在这种情况下释放每个 Io Obj 是否安全?

我还发现,当我在同一台机器上运行我的服务器应用程序和客户端应用程序时,这种情况发生得更频繁。有时会发生服务器无法完成写入操作的情况。我认为这是因为客户端接收缓冲区已满。 (客户端不会停止接收数据!)。

剪掉的代码会尽快跟上。

【问题讨论】:

请提供minimal reproducible example。 如果接收方没有收到,当然发送方会阻塞,除非他处于非阻塞模式,而 IOCP 不是。您的测试无效。 @EJP 这是否意味着,我无法事先检测到这一点?似乎WSASend() 可以发布,但永远不会完成。如何改进测试? ERROR_NOT_FOUND 可能意味着有问题的套接字上没有任何挂起的 I/O。这可能意味着 Windows 或您的代码已删除原始完成通知,它更有可能是您的代码。 您的测试客户端必须读取套接字上所有可用的内容。 【参考方案1】: “linger”设置可用于重置连接,但这样您将 (a) 丢失数据并 (b) 向对等方发送重置,这可能会吓到对方。 如果您正在考虑积极的延迟超时,它并没有真正的帮助。 关闭读取应该会终止读取操作,但关闭写入只会在挂起的写入之后排队,所以它根本没有帮助。 如果挂起的写入是问题,并且未完成,则必须取消它们。

【讨论】:

感谢您的回答。我不会关心数据丢失。我的应用程序意图是客户端通常应该“永远”连接并循环发送相同的数据,因此如果客户端失去连接并留下半开的套接字,我可以直接杀死它。对我来说重要的是:应用程序应该能够恢复内存和一切以便正常运行。 “恐吓同伴”是什么意思? 等一下。如果客户端关闭连接,服务器的read() 将返回-1,然后您关闭通道。您根本不必对此采取任何特殊措施:它只是网络编程的正常部分。 “吓坏对等方”意味着它将重置连接,而不是刚刚描述的通常的流结束:但如果是客户端正在关闭,那当然不适用。在我看来,您的 I/O 操作无法完成的唯一方法必须是它们正在等待写入。现在看来,您的读取处理代码中存在错误。 很抱歉这么长的响应时间。最后,就像你们都建议的那样:我是问题所在。 closesocket() 的功能符合预期。我有一次问题是一个连接没有正确结束,但我无法重现它。我将此标记为答案,因为我认为这是对上述可能性的一个很好的总结。我想补充一件事:如果您开发 IOCP 应用程序,您还应该注意部分发送(因为它们通常不会发生)并在发生这种情况时终止连接。在大多数示例中,没有人关心这一点,但这很重要。

以上是关于closesocket() 未完成 IOCP 的挂起操作的主要内容,如果未能解决你的问题,请参考以下文章

如何强制系统关闭底层套接字

IOCP:如果操作立即返回错误,我还能收到完成通知吗?

shoutdown()与closesocket()区别

GetQueuedCompletionStatus 延迟

未使用 WinRT 引发的挂起事件

Windows完成端口 IOCP模型