WSAEnumNetworkEvents之后对WSAResetEvent的调用是否应该导致事件不再设置?

时间:2018-12-11 17:00:33

标签: c++ c winsock winsock2

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

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);时,有时程序可以运行并从服务器读取所有数据,但是有时,即使服务器已经收到WSA_WAIT_TIMEOUT,它似乎也陷入了循环中似乎有排队的数据。

当程序循环接收WSA_WAIT_TIMEOUT时,Process Hacker显示套接字以正常状态连接。

现在我们知道WSAEnumNetworkEvents将重置hEventSocket,但似乎对WSAResetEvent的附加调用似乎没有受到伤害。永久弄乱信令也没有意义。我希望也许不会收到关于要读取的最后一块数据的通知,因为在调用WSAEnumNetworkEventsWSAResetEvent之间可能已经读取了数据,但是我假设一旦套接字上有其他数据,则hEventSocket会被引发。

其中一个比较奇怪的部分是我们已经运行了多年的代码,现在才看到这个问题。

有什么想法会引起问题吗?

2 个答案:

答案 0 :(得分:1)

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

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

最好让WSAEnumNetworkEvents处理事件状态。

答案 1 :(得分:1)

手动调用WSAResetEvent()会导致竞争状态,这可能会使您的套接字进入错误状态。

在调用WSAEnumNetworkEvents()之后,如果之后有新数据到达,或者以前的读取还剩下未读取的数据,则将发出事件信号,但仅当套接字位于正确时状态来表示该事件。

如果在致电WSAResetEvent()之前确实通知了该事件,则会丢失该信号。

根据WSAEventSelect()文档:

  

已经成功记录了网络事件的发生(通过设置内部网络事件记录中的相应位)并用信号通知了相关的事件对象,在应用程序执行该功能之前,对该网络事件不采取进一步的操作

>隐式重新启用该网络事件的设置和相关事件对象的信号通知的呼叫。

     

FD_READ   recv,recvfrom,WSARecv,WSARecvEx或WSARecvFrom函数。

     

...

     

对重新启用例程的任何调用,即使是失败的调用,也会导致重新启用相关网络事件和事件对象的记录和信令

     

...

     

对于FD_READ,FD_OOB和FD_ACCEPT网络事件,将对网络事件记录和事件对象信令进行级别触发。这意味着如果调用重新启用例程并且在调用之后相关的网络条件仍然有效,则记录网络事件并设置关联的事件对象。这样一来,应用程序就可以由事件驱动,而不必担心任何时候到达的数据量

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

通过手动重置事件,您丢失了信号,该信号使WSAWaitForMultipleEvents()可以告诉您进行呼叫 WSAEnumNetworkEvents(),以便随后可以告诉您从套接字读取。如果不进行该读取,则在等待读取数据时将永远不会再次通知该事件。您注册的唯一可以表明该事件的条件是套接字关闭。

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