如何在断开连接后干净地重新连接 boost::socket?

新手上路,请多包涵

我的客户端应用程序使用 boost::asio::ip::tcp::socket 连接到远程服务器。如果应用程序失去与此服务器的连接(例如,由于服务器崩溃或关闭),我希望它定期尝试重新连接,直到成功。

我需要在客户端做什么才能干净地处理断开连接,整理然后反复尝试重新连接?

目前我的代码中有趣的部分看起来像这样。

connect 像这样:

 bool MyClient::myconnect()
{
    bool isConnected = false;

    // Attempt connection
    socket.connect(server_endpoint, errorcode);

    if (errorcode)
    {
        cerr << "Connection failed: " << errorcode.message() << endl;
        mydisconnect();
    }
    else
    {
        isConnected = true;

        // Connected so setup async read for an incoming message.
        startReadMessage();

        // And start the io_service_thread
        io_service_thread = new boost::thread(
            boost::bind(&MyClient::runIOService, this, boost::ref(io_service)));
    }
    return (isConnected)
}

runIOServer() 方法只是:

 void MyClient::runIOService(boost::asio::io_service& io_service)
{
    size_t executedCount = io_service.run();
    cout << "io_service: " << executedCount << " handlers executed." << endl;
    io_service.reset();
}

如果任何异步读取处理程序返回错误,那么他们只需调用此 disconnect 方法:

 void MyClient::mydisconnect(void)
{
    boost::system::error_code errorcode;

    if (socket.is_open())
    {
        // Boost documentation recommends calling shutdown first
        // for "graceful" closing of socket.
        socket.shutdown(boost::asio::ip::tcp::socket::shutdown_both, errorcode);
        if (errorcode)
        {
            cerr << "socket.shutdown error: " << errorcode.message() << endl;
        }

        socket.close(errorcode);
        if (errorcode)
        {
            cerr << "socket.close error: " << errorcode.message() << endl;
        }

        // Notify the observer we have disconnected
        myObserver->disconnected();
    }

..它尝试优雅地断开连接,然后通知观察者,观察者将开始以五秒的间隔调用 connect() 直到重新连接。

还有什么我需要做的吗?

目前这 似乎 确实有效。如果我杀死它所连接的服务器,我会在我的读取处理程序中得到预期的 "End of file" 错误,并且调用 mydisconnect() 没有任何问题。

但是当它尝试重新连接并失败时,我看到它报告 "socket.shutdown error: Invalid argument" 。这仅仅是因为我试图关闭一个没有读/写挂起的套接字吗?还是更多?

原文由 GrahamS 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 1.8k
2 个回答

每次重新连接时,您都需要创建一个新的 boost::asio::ip::tcp::socket 。最简单的方法可能是使用 boost::shared_ptr 在堆上分配套接字(如果你的套接字完全封装在一个类中,你也可以使用 scoped_ptr ) .例如:

 bool MyClient::myconnect()
{
    bool isConnected = false;

    // Attempt connection
    // socket is of type boost::shared_ptr<boost::asio::ip::tcp::socket>
    socket.reset(new boost::asio::ip::tcp::socket(...));
    socket->connect(server_endpoint, errorcode);
    // ...
}

然后,当调用 mydisconnect 时,您可以释放套接字:

 void MyClient::mydisconnect(void)
{
    // ...
    // deallocate socket.  will close any open descriptors
    socket.reset();
}

您看到的错误可能是操作系统在您调用 close 后清理文件描述符的结果。当您在同一个套接字上调用 close 然后尝试 connect 时,您可能正在尝试连接无效的文件描述符。此时,根据您的逻辑,您应该会看到一条以“连接失败:…”开头的错误消息,但随后您调用 mydisconnect 这可能正在尝试调用 shutdown 无效的文件描述符。恶性循环!

原文由 bjlaub 发布,翻译遵循 CC BY-SA 2.5 许可协议

我过去使用 Boost.Asio 做过类似的事情。我使用异步方法,因此重新连接通常是让我现有的 ip::tcp::socket 对象超出范围,然后为调用 async_connect 创建一个新对象。如果 async_connect 失败,我会使用一个计时器来休眠一下,然后重试。

原文由 Sam Miller 发布,翻译遵循 CC BY-SA 2.5 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题