Twisted includes a reactor implemented on top of MsgWaitForMultipleObjects
. Apparently the reactor has problems reliably noticing when a TCP connection ends, at least in the case where a peer sends some bytes and then quickly closes the connection. What seems to happen is:
- The reactor calls
MsgWaitForMultipleObjects
with some socket handles andQS_ALLINPUT
. - The call completes and indicates the handle for a socket in this state (that is, has bytes waiting to be read and has been closed by the peer) is active.
- The reactor dispatches this notification to the common TCP implementation.
- The TCP implementation reads the available bytes from the socket. There are some, they get delivered to application co开发者_C百科de.
- Control is returned to the reactor, which eventually calls
MsgWaitForMultipleObjects
again. MsgWaitForMultipleObjects
never again indicates that the handle is active. The TCP implementation never gets to look at the socket again, so it can never detect that the connection is closed.
This makes it appear as though MsgWaitForMultipleObjects
is an edge-triggered notification mechanism. The MSDN documentation says:
Waits until one or all of the specified objects are in the signaled state
or the time-out interval elapses.
This doesn't sound like edge-triggering. It sounds like level-triggering.
Is MsgWaitForMultipleObjects
actually edge-triggered? Or is it level-triggered and this misbehavior is caused by some other aspect of its behavior?
Addendum The MSDN docs for WSAEventSelect explains what's going on here a bit more, including pointing out that FD_CLOSE
is basically a one-off event. After its signaled once, you'll never get it again. This goes some way towards explaining why Twisted has this problem. I'm still interested to hear how to effectively use MsgWaitForMultipleObjects
given this limitation, though.
In order to use WSAEventSelect
and differentiate activities, you need to call WSAEnumNetworkEvents
. Make sure you're processing each event that was reported, not just the first.
WSAAsyncSelect
makes it easy to determine the cause, and is often used together with MsgWaitForMultipleObjects
.
So you might use WSAAsyncSelect
instead of WSAEventSelect
.
Also, I think you have a fundamental misunderstanding of the difference between edge-triggered and level-triggered. Your reasoning seems to be more related to auto-reset vs manual-reset events.
精彩评论