如何检测Boost TCP套接字断开连接

16

假设我有一个套接字:

std::shared_ptr<tcp::socket> socket( new tcp::socket(acceptor.get_io_service()) );
acceptor.async_accept( *socket, std::bind( handleAccept, this, std::placeholders::_1, socket, std::ref(acceptor)) );

我在一个容器中存储了该套接字的weak_ptr。我需要这样做是因为我希望允许客户端请求其他客户端的列表,以便它们可以互相发送消息。

clients_.insert(socket);  // pseudocode

然后我运行一些异步操作

socket->async_receive( boost::asio::buffer(&(*header), sizeof(Header))
    , 0
    , std::bind(handleReceiveHeader, this, std::placeholders::_1, std::placeholders::_2, header, socket));

我该如何检测连接何时关闭,以便我可以从容器中移除我的套接字?

clients_.erase(socket); // pseudocode
2个回答

25

asio中,TCP套接字的断开通常通过eofconnection_reset信号来表示。例如:

  void async_receive(boost::system::error_code const& error,
                     size_t bytes_transferred)
  {
    if ((boost::asio::error::eof == error) ||
        (boost::asio::error::connection_reset == error))
    {
      // handle the disconnect.
    }
    else
    {
       // read the data 
    }
  }

我使用boost::signals2来信号断开,然而你也可以将指向函数的指针传递给socket类并调用它。

注意socket和回调函数的生存周期,请参见:boost-async-functions-and-shared-ptrs


2
错误列表可以在这里找到:http://www.boost.org/doc/libs/1_44_0/doc/html/boost_asio/reference/error__basic_errors.html - aCuria
我发现这确实有效,但是我收到了“connection_reset”错误。然而,在文档中我注意到另一个错误代码,“connection_aborted”。是否有必要处理这个错误代码? - aCuria
谢谢,这很有用,但我想知道为什么在一个简单的boost::asio::ssl [客户端-服务器应用程序] (http://www.boost.org/doc/libs/1_37_0/doc/html/boost_asio/example/ssl/server.cpp)中,当我发送`kill -9 <pid_client>时,我直接进入session :: ~ session()而不是对应于您示例的session :: handle_read(...)`函数。 - Vitaly Isaev
4
好的。但如果你不想接收任何数据呢?我唯一想知道的是连接是否正常,而不想尝试读取远程方发送的任何内容。看起来在asio中没有这样的API。 - Oleg Andriyanov
2
@OlegAndriyanov,你的需求听起来相当不寻常。这是一种通信协议,因此发送和接收数据是它存在的理由... - kenba
显示剩余2条评论

1
有很多选择,其中一些包括:
1. 将 `weak_ptr` 存储在容器中 - 这不会延长套接字的生命周期,因此当处理程序收到 `boost::asio::error::eof`(或其他错误)时,它不会复制/移动 `shared_ptr`,并且套接字将被删除(如果您没有其他 `shared_ptr` 指向它)。所以,您可以这样做:`if(socket.expired()) clients_.erase(socket);`
2. 在处理程序中检查错误代码 - 它将指示连接何时关闭。使用此信息 - 您可以从处理程序本身调用 `clients_.erase`。
“你能给出第二种方法的例子吗?”
它将类似于:
socket->async_receive
(
    boost::asio::buffer(&(*header), sizeof(Header)), 0,
    [=, &clients_](const system::error_code& error, size_t bytes_transferred)
    {
        if(error) // or some specific code
        {
            clients_.erase(socket); // pseudocode
        }
        else
        {
            // continue, launch new operation, etc
        }
    }
);

你能给出第二个例子吗? - aCuria
@aCuria 添加了示例。 - Evgeny Panasyuk
我认为在任何错误情况下都不应该擦除,快速的谷歌搜索似乎没有列出专门针对async_receive的错误代码列表。 - aCuria
与关闭连接相关的错误:boost::asio::error::eof,与重置连接相关的错误:boost::asio::error::connection_reset。 - kevr

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接