【问题标题】:Winsock accept event sometimes stops signaling (WSAEventSelect)Winsock 接受事件有时会停止信号 (WSAEventSelect)
【发布时间】:2010-01-29 13:11:43
【问题描述】:

我对属于多线程套接字服务器的一段遗留 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() 的调用。

【问题讨论】:

    标签: c++ windows events winsock


    【解决方案1】:

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

    1. 事件信号,WaitForMultipleObjects() 返回
    2. 在 HandleConnect() 期间,在调用 accept() 后的某个时间,再次发出事件信号
    3. HandleConnect() 返回
    4. ResetEvent() 重置事件,屏蔽第二个信号
    5. WaitForMultipleObjects() 永远不会返回,因为就 Windows 而言,它已经发出了后续事件的信号,并且没有后续的 accept() 重新启用它

    几个可能的解决方案:1) 在 HandleConnect() 中循环 accept() 直到返回 WSAEWOULDBLOCK 2) 使用自动重置事件或在调用 HandleConnect() 之前立即重置事件

    【讨论】:

    • 我终于有时间尝试您的建议,它似乎已经解决了问题。非常感谢您的帮助!作为记录,我将代码更改为使用自动重置事件,如您帖子中的第二个解决方案中所述。
    【解决方案2】:

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

    【讨论】:

    • 看起来 WSAWaitForMultipleObjects() 只是 WaitForMultipleObjects() 的包装,所以我不确定这是否是问题所在。我很感谢你的建议,但约翰。
    【解决方案3】:

    从阅读the docs 来看,WSAEventSelect() 似乎与WSAAsyncSelect() 一样对通知很吝啬。每次连接进入时,堆栈都不会发出FD_ACCEPT 信号。对于 Winsock,通知是它的表达方式:

    您之前调用了accept(),但它以WSAEWOULDBLOCK 失败。再调用一次吧,这次应该成功了。

    解决方法是在调用WSAEventSelect()之前调用accept(),在得到WSAEWOULDBLOCK之后才调用WSAEventSelect()。为此,您需要将侦听套接字设置为非阻塞。 (这似乎很明显,但实际上并不是必需的。)

    【讨论】:

    • Warren - 感谢您抽出宝贵时间回复。我应该说套接字是非阻塞的。发布的代码可以正常工作几天,响应所有连接尝试,然后在任何客户端尝试连接时突然停止发出事件信号。我开始怀疑服务器上的配置问题或资源泄漏。
    【解决方案4】:

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

    【讨论】:

      猜你喜欢
      • 2011-03-27
      • 2014-03-07
      • 2015-08-05
      • 2023-03-17
      • 1970-01-01
      • 1970-01-01
      • 2017-02-23
      • 2018-02-20
      • 1970-01-01
      相关资源
      最近更新 更多