Winsock 接受事件有时会停止发送信号(WSAEventSelect)

发布于 2024-08-19 13:51:23 字数 1394 浏览 10 评论 0原文

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

I have a problem with a piece of legacy c++/winsock code that is part of a multi-threaded socket server. The application creates a thread that handles connections from clients, of which there are typically a couple of hundred connected at any one time. It typically runs without a problem for several days (continuously), and then suddenly stops accepting connections. This only happens in production, never test.

It uses WSAEventSelect() to detect FD_ACCEPT network events. The (simplified) code for the connection handler is:

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;
    }
}

From detailed logging I know that, when the problem occurs, the thread enters WaitForMultipleObjects() and never emerges, even though there are clients attempting to connect and waiting for an accept. The WAIT_FAILED and WAIT_ABANDONED_x conditions never occur.

While I haven't ruled-out a config problem on the server, or even some kind of resource leak (can't find anything), I am also wondering if the event created by WSACreateEvent() is somehow being 'dissassociated' from the FD_ACCEPT network event - causing it to never fire.

So, am I doing something wrong here? Is there something I should be doing that I'm not? Or a better way? I'd appreciate any suggestions! Thanks.

EDIT

The socket is a non-blocking socket.

EDIT

Problem solved by using the approach suggested by kipkennedy (below). Changed hAcceptEvent to be an auto-reset event, and removed the call to WSAResetEvent() which was no-longer needed.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

说不完的你爱 2024-08-26 13:51:23

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

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

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

Maybe an FD_ACCEPT is signaling during HandleConnect() after the accept() and before the return and subsequent ResetEvent(). Then, ResetEvent() ends up resetting all signals and no re-enabling accept() is ever called. For example, the following sequence is possible:

  1. Event signaled, WaitForMultipleObjects() returns
  2. During HandleConnect(), sometime after accept() is called, the Event is signaled again
  3. HandleConnect() returns
  4. ResetEvent() resets the event, masking the second signal
  5. WaitForMultipleObjects() never returns since as far as Windows is concerned, it has already signaled the subsequent event and no subsequent accepts() have re-enabled it

A couple possible solutions: 1) loop on accept() in HandleConnect() until WSAEWOULDBLOCK is returned 2) use an auto-reset event or immediately reset the event before calling HandleConnect()

狼性发作 2024-08-26 13:51:23

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

Code looks fine. The only thing I can suggest is calling WSAWaitForMultipleObjects() instead of the global version.

左岸枫 2024-08-26 13:51:23

通过阅读文档,似乎< code>WSAEventSelect() 对于通知的处理与 WSAAsyncSelect() 一样简洁。每次有连接进入时,堆栈不会发出 FD_ACCEPT 信号。对于 Winsock 来说,通知是它的表达方式:

您之前调用了 accept(),但失败并显示 WSAEWOULDBLOCK。再调用一次,这次应该会成功。

解决方案是在调用 WSAEventSelect() 之前调用 accept(),并且仅在获取 WSAEWOULDBLOCK 后调用 WSAEventSelect()代码>.为了使其按预期工作,您需要将侦听套接字设置为非阻塞。 (这似乎是显而易见的,但实际上并不是必需的。)

From reading the docs, it appears that WSAEventSelect() is as parsimonious about notifications as WSAAsyncSelect(). The stack doesn't signal FD_ACCEPT every time a connection comes in. To Winsock, the notification is its way of saying:

You called accept() earlier, and it failed with WSAEWOULDBLOCK. Go ahead and call it again, it should succeed this time.

The solution is to call accept() before calling WSAEventSelect(), and only call WSAEventSelect() after you get WSAEWOULDBLOCK. For this to work as you expect, you need to have set the listening socket to non-blocking. (That might seem obvious, but it isn't actually required.)

烟酉 2024-08-26 13:51:23

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

After an accept event occured you must not do a WSAResetEvent(hAcceptEven). You must issue a WSAEnumNetworkEvents ( listener, hAcceptEvent, &some_struct). This functions clears the internal state of the socket (ar copies this state into some_struct) and after that you can receive new connections.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文