【问题标题】:Boost and Windows sockets - Properly handling TCP client disconnect scenariosBoost 和 Windows 套接字 - 正确处理 TCP 客户端断开连接场景
【发布时间】:2015-01-28 04:59:51
【问题描述】:

我有一个名为 ServerConnectionHandler 的类,它创建了一个用于从服务器读取数据的 boost 线程。 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。我不想通过任何锁定来减慢接收器线程的速度,因为性能至关重要。根据我的阅读,我看到人们创建了一个 volatile bool 标志,告诉接收线程停止。我用这种方法看到的问题是,如果它在调用析构函数时正在处理消息(m_tcpEventHandler.OnClientMessage())怎么办?然后它可以立即命中被破坏对象的代码(m_tcpEventHandler 可以反过来使用 ServerConnectionHandler 的成员变量或方法)。我想不出一个干净的方法来处理所有 3 个案例。

【问题讨论】:

  • 在你的析构函数中等待线程退出。

标签: c++ multithreading sockets boost


【解决方案1】:

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

【讨论】:

    【解决方案2】:

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

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

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-01-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-03-25
      • 1970-01-01
      • 2010-12-20
      相关资源
      最近更新 更多