加速和 Windows 套接字 - 正确处理 TCP 客户端断开连接方案

Boost and Windows sockets - Properly handling TCP client disconnect scenarios

本文关键字:客户端 断开 连接 方案 TCP 正确处理 Windows 套接字 加速      更新时间:2023-10-16

我有一个名为ServerConnectionHandler的类,它创建了一个用于从服务器读取数据的提升线程。boost 线程绑定到 ServerConnectionHandler 对象。相关代码如下:

ServerConnectionHandler::~ServerConnectionHandler()
{
    close();
}
void ServerConnectionHandler::close()
{
    closesocket(m_ConnectSocket);
    WSACleanup();
}
void ServerConnectionHandler::MsgLoop()
{
    int size_recv = 0;
    char chunk[DEFAULT_BUFLEN];
    while(1)
    {
        memset(chunk, 0, DEFAULT_BUFLEN);
        size_recv = recv(m_ConnectSocket, chunk, DEFAULT_BUFLEN, 0);
        if(size_recv > 0)
        {
            for( int i=0; i < size_recv; ++i )
            {
                if(chunk[i] == 'n')
                {
                    m_tcpEventHandler.OnClientMessage(m_RecBuffer);
                    m_RecBuffer.clear();
                }
                else
                {
                    m_RecBuffer.append(1, chunk[i]);
                }
             }  
          }
          else if(size_recv == 0)
          {
              close();
              const std::string error = "MsgReceiver Received 0 bytes because connection was closed. MsgReceiver shutting down.n";
              m_tcpEventHandler.OnClientSocketError(error);
              break;
          }
          else
          {
              char error [512];
              sprintf(error, "Error on Receiving Socket. Recv=[%d], WSAError=[%d]. MsgReceiver shutting down.n", size_recv, WSAGetLastError());
              m_tcpEventHandler.OnClientSocketError(error);
              close();
              break;
          }
       }
       // NOTE: This will eventually call the destructor of ServerConnectionHandler...
       m_tcpEventHandler.OnClientDisconnect("Disconnected. Reason: Remote host snapped connection.");
 }

我的问题是,当在析构函数中调用 close() 时,接收器线程仍在运行,并且在尝试调用任何 m_tcpEventHandler.OnClient...() 方法,因为此时对象已被销毁。

我需要能够在 3 种不同的情况下干净地处理这个问题:

  1. 当用户手动断开客户端的连接时(在这种情况下将调用析构函数)。
  2. 当客户端与服务器断开连接时(例如,可能是因为服务器崩溃)。
  3. 当应用程序关闭时(需要完全断开所有连接 - 类似于#1)。

目前,此代码仅适用于案例 #2。我不想通过任何锁定来减慢接收器线程的速度,因为性能至关重要。从我所读到的内容来看,我看到人们创建了一个易失性的布尔标志,告诉接收线程停止。我用这种方法看到的问题是,如果在调用析构函数时它正在处理消息(m_tcpEventHandler.OnClientMessage())怎么办?然后它可以立即命中已销毁对象的代码(m_tcpEventHandler反过来可以使用ServerConnectionHandler的成员变量或方法)。我想不出一种干净的方式来处理这里的所有 3 个案例。

在关闭析构函数中的套接字之前,请将其关闭以进行输入。这将导致接收线程获得流的结束并很好地退出。您可能希望在最终关闭之前在 dtor 和接收器线程之间添加一点握手,或者您可能只想依靠接收器线程关闭套接字,而不是在 dtor 中关闭它。

"

我的问题是当在析构函数中调用 close() 时,接收器线程仍在运行" - 在我看来,您的问题只是线程同步。与人脉关系不大。

使通信异步可以更好地控制接收线程。

例如,您可以使用Boost Asio进行异步套接字读取(当然还有写入)。如果将"无限"deadline_timer添加到异步队列中,则可以cancel()该计时器,接收线程可以使用它来停止接收并执行更多清理(例如,将"再见"消息写入远程端)。

(如果不需要后者,只需关闭io_service即可取消所有异步操作。这将是相当不礼貌的,但在快速关闭路径中并不是一个坏主意。