Winsock接受事件有时会停止信令(WSAEventSelect)

时间:2010-01-29 13:11:44

标签: c++ windows events winsock

我遇到了一个遗留的c ++ / winsock代码问题,它是多线程套接字服务器的一部分。应用程序创建一个处理来自客户端的连接的线程,其中任何时候通常连接几百个。它通常连续几天(连续)运行没有问题,然后突然停止接受连接。这只发生在生产中,从不测试。

它使用WSAEventSelect()来检测FD_ACCEPT网络事件。连接处理程序的(简化)代码是:

SOCKET listener;
HANDLE hStopEvent;

// ... initialise listener and hStopEvent, and other stuff ...

HANDLE hAcceptEvent = WSACreateEvent();
WSAEventSelect(listener, hAcceptEvent, FD_ACCEPT); 
HANDLE rghEvents[] = { hStopEvent, hAcceptEvent };

bool bExit = false;
while(!bExit)
{
    DWORD nEvent = WaitForMultipleObjects(2, rghEvents, FALSE, INFINITE);
    switch(nEvent)
    {
        case WAIT_OBJECT_0:
            bExit = true;
            break;
        case WAIT_OBJECT_1:
            HandleConnect();
            WSAResetEvent(hAcceptEvent);
            break;
        case WAIT_ABANDONED_0:
        case WAIT_ABANDONED_0 + 1:
        case WAIT_FAILED:
            LogError();
            break;
    }
}

从详细日志记录中我知道,当问题发生时,线程进入WaitForMultipleObjects()并且永远不会出现,即使有客户端尝试连接并等待接受。 WAIT_FAILED和WAIT_ABANDONED_x条件永远不会发生。

虽然我没有排除服务器上的配置问题,甚至某种资源泄漏(找不到任何东西),但我也想知道WSACreateEvent()创建的事件是否以某种方式被“解除关联” '来自FD_ACCEPT网络事件 - 导致它永远不会开火。

那么,我在这里做错了吗?有什么我应该做的,我不是吗?还是更好的方法?我很感激任何建议!感谢。

修改

套接字是一个非阻塞套接字。

修改

使用kipkennedy(下文)建议的方法解决了问题。将hAcceptEvent更改为自动重置事件,并删除了对不再需要的WSAResetEvent()的调用。

4 个答案:

答案 0 :(得分:4)

在accept()之后以及返回和随后的ResetEvent()之前,可能在HandleConnect()期间发出FD_ACCEPT信号。然后,ResetEvent()最终重置所有信号,并且不会调用重新启用的accept()。例如,以下序列是可能的:

  1. 发出事件信号,WaitForMultipleObjects()返回
  2. 在HandleConnect()期间,在调用accept()之后的某个时间,再次发出事件信号
  3. HandleConnect()返回
  4. ResetEvent()重置事件,屏蔽第二个信号
  5. WaitForMultipleObjects()永远不会返回,因为就Windows而言,它已经发出了后续事件的信号,并且没有后续的accept()重新启用它
  6. 一些可能的解决方案:1)在HandleConnect()中的accept()循环,直到返回WSAEWOULDBLOCK 2)使用自动重置事件或在调用HandleConnect()之前立即重置事件

答案 1 :(得分:1)

代码看起来很好。我唯一可以建议的是调用WSAWaitForMultipleObjects()而不是全局版本。

答案 2 :(得分:1)

从阅读the docs开始,WSAEventSelect()似乎与WSAAsyncSelect()的通知一样简洁。每次连接进入时,堆栈都不会发出FD_ACCEPT信号。对于Winsock,通知是这样说的:

  

您之前致电accept(),但WSAEWOULDBLOCK失败了。继续再打电话,这次应该会成功。

解决方法是在致电accept()之前致电WSAEventSelect(),然后在获得WSAEventSelect()后致电WSAEWOULDBLOCK。为了使其按预期工作,您需要将侦听套接字设置为非阻塞。 (这看起来很明显,但实际上并不需要。)

答案 3 :(得分:1)

发生接受事件后,您不能执行WSAResetEvent(hAcceptEven)。您必须发出WSAEnumNetworkEvents(listener,hAcceptEvent和& some_struct)。此函数清除套接字的内部状态(将此状态复制到some_struct中),之后您可以接收新连接。