如何通过MsgWaitForMultipleObjects可靠检测断开连接的TCP套接字?

6 投票
1 回答
721 浏览
提问于 2025-04-17 03:21

Twisted库里有一个基于 MsgWaitForMultipleObjects 实现的反应器。看起来这个反应器在可靠地检测TCP连接结束时有些问题,尤其是在对方发送了一些数据后又迅速关闭连接的情况下。事情的经过大致是这样的:

  1. 反应器调用 MsgWaitForMultipleObjects,传入一些套接字句柄和 QS_ALLINPUT
  2. 这个调用完成后,显示某个套接字的句柄处于活动状态(也就是说,有数据等待读取,并且对方已经关闭了连接)。
  3. 反应器将这个通知传递给常见的TCP实现。
  4. TCP实现从套接字中读取可用的数据。有一些数据,它们被传递给应用程序代码。
  5. 控制权返回给反应器,反应器最终又调用 MsgWaitForMultipleObjects
  6. MsgWaitForMultipleObjects 再也没有显示这个句柄是活动的。TCP实现再也无法查看这个套接字,因此无法检测到连接已经关闭。

这让人觉得 MsgWaitForMultipleObjects 是一种边缘触发的通知机制。MSDN文档上说:

Waits until one or all of the specified objects are in the signaled state
or the time-out interval elapses.

这听起来不像是边缘触发,反而更像是水平触发。

那么 MsgWaitForMultipleObjects 真的算是边缘触发吗?还是说它是水平触发的,而这种异常行为是由其他方面引起的呢?

补充说明 MSDN关于WSAEventSelect的文档对此进行了更详细的解释,指出 FD_CLOSE 基本上是一个一次性的事件。它被触发一次后,就再也不会出现了。这在一定程度上解释了为什么Twisted会有这个问题。不过,我仍然想知道在这种限制下,如何有效地使用 MsgWaitForMultipleObjects

1 个回答

1

为了使用 WSAEventSelect 并区分不同的活动,你需要调用 WSAEnumNetworkEvents。确保你处理每一个报告的事件,而不仅仅是第一个。

WSAAsyncSelect 可以帮助你轻松判断事件的原因,通常和 MsgWaitForMultipleObjects 一起使用。

所以你可能会选择使用 WSAAsyncSelect 来代替 WSAEventSelect

另外,我觉得你对边缘触发和电平触发之间的区别有些误解。你的推理似乎更像是在讨论自动重置和手动重置事件。

撰写回答