在 WSAEnumNetworkEvents 之后调用 WSAResetEvent 是否会导致事件不再被设置?

Should a call to WSAResetEvent after WSAEnumNetworkEvents cause event to never be set again?

我们有一个正在读取套接字的线程。我们 运行 在网络上遇到了一个问题,延迟比我们习惯的要长一些,我们的读取循环似乎会停止接收套接字上读取事件的通知。原始代码(删除了一些错误检查):

HANDLE hEventSocket = WSACreateEvent();
WSAEventSelect(pIOParams->sock, hEventSocket, FD_READ | FD_CLOSE);
std::array<HANDLE, 2>   ahEvents;

// This is an event handle that can be called from another thread to 
// get this read thread to exit
ahEvents[0] = pIOParams->hEventStop; 
ahEvents[1] = hEventSocket;
while(pIOParams->bIsReading)
{
    // wait for stop or I/O events
    DWORD dwTimeout = 30000; // in ms
    dwWaitResult = WSAWaitForMultipleEvents(ahEvents.size(), ahEvents.data(), FALSE, dwTimeout, FALSE);
    if(dwWaitResult == WSA_WAIT_TIMEOUT)
    {
        CLogger::LogPrintf(LogLevel::LOG_DEBUG, "CSessionClient", "WSAWaitForMultipleEvents time out");
        continue;  
    }
    if(dwWaitResult == WAIT_OBJECT_0) // check to see if we were signaled to stop from another thread
    {
         break;
    }
    if(dwWaitResult == WAIT_OBJECT_0 +1)
    {
        // determine which I/O operation triggered event
        if (WSAEnumNetworkEvents(pIOParams->sock, hEventSocket, &NetworkEvents) != 0)
        {
            int err = WSAGetLastError();
            CLogger::LogPrintf(LogLevel::LOG_WARN, "CSessionClient", "WSAEnumNetworkEvents failed (%d)", err);
            break;
        }

        // HERE IS THE LINE WE REMOVED THAT SEEMED TO FIX THE PROBLEM
        WSAResetEvent(hEventSocket);

        // Handle events on socket
        if (NetworkEvents.lNetworkEvents & FD_READ)
        {
             // Do stuff to read from socket
        }
        if (NetworkEvents.lNetworkEvents & FD_CLOSE)
        {
             // Handle that the socket was closed
             break;
        }
    }

}

这里是问题所在:代码中有 WSAResetEvent(hEventSocket);,有时程序可以运行并从服务器读取所有数据,但有时,它似乎陷入了接收 [=30= 的循环中], 即使服务器似乎有数据排队等候它。

在程序循环接收的同时WSA_WAIT_TIMEOUT,Process Hacker显示socket连接正常

现在我们知道 WSAEnumNetworkEvents 会重置 hEventSocket,但是额外调用 WSAResetEvent 似乎不会有什么坏处。它也没有意义,它永久地弄乱了信号。我希望也许我们不会收到要读取的最后一块数据的通知,因为数据可能已在调用 WSAEnumNetworkEventsWSAResetEvent 之间读取,但我假设一旦额外的数据进入套接字,hEventSocket 会被提升。

最重要的是运行我们已经运行这个代码很多年了,我们现在才看到这个问题。

知道为什么这会导致问题吗?

您已经将事件句柄传递给 WSAEnumNetworkEvents,它以原子方式重置句柄。也就是说,只有在复制未决事件数据时才会重置句柄。

直接调用 WSAResetEvent 可能会丢失数据通知(即您调用 WSAEnumNetworkEvents 以获取当前状态并重置事件,之后更多数据到达导致事件被设置但在您之前调用 WSAResetEvent,然后在下一次循环迭代之前调用 WSAResetEvent,除非有更多数据进入,否则您不会被告知已经进入的数据。

让 WSAEnumNetworkEvents 处理事件状态要好得多。

手动调用 WSAResetEvent() 会引入竞争条件,使您的套接字处于错误状态。

在调用 WSAEnumNetworkEvents() 之后,当新数据随后到达,或者有先前读取遗留下来的未读数据时,将发出事件信号,但仅当套接字位于 适当的 状态来发出该事件的信号。

如果事件在您调用 WSAResetEvent() 之前收到信号,您将失去该信号。

根据 WSAEventSelect() 文档:

Having successfully recorded the occurrence of the network event (by setting the corresponding bit in the internal network event record) and signaled the associated event object, no further actions are taken for that network event until the application makes the function call that implicitly reenables the setting of that network event and signaling of the associated event object.

FD_READ The recv, recvfrom, WSARecv, WSARecvEx, or WSARecvFrom function.

...

Any call to the reenabling routine, even one that fails, results in reenabling of recording and signaling for the relevant network event and event object.

...

For FD_READ, FD_OOB, and FD_ACCEPT network events, network event recording and event object signaling are level-triggered. This means that if the reenabling routine is called and the relevant network condition is still valid after the call, the network event is recorded and the associated event object is set. This allows an application to be event-driven and not be concerned with the amount of data that arrives at any one time

这意味着如果您在调用 WSAEnumNetworkEvents() 后手动重置事件,则在您对套接字执行读取(重新启用事件签名)之前,不会再次发出事件信号用于读取操作)并且之后有新数据到达,或者您没有读取所有可用数据。

通过手动重置事件,您将失去允许 WSAWaitForMultipleEvents() 告诉您呼叫的信号 WSAEnumNetworkEvents() 这样它就可以告诉您从套接字中读取。如果没有读取,当数据等待读取时,事件将永远不会再次发出信号。您注册的唯一可以发出事件信号的其他条件是套接字关闭。

由于 WSAEnumNetworkEvents() 已经为您重置了活动,请勿手动重置活动!