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

Gra*_*amS 27 c++ sockets boost boost-asio

我的客户端应用程序使用a 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)
}
Run Code Online (Sandbox Code Playgroud)

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();
}
Run Code Online (Sandbox Code Playgroud)

如果任何异步读取处理程序返回错误,那么他们只是调用此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();            
    }
Run Code Online (Sandbox Code Playgroud)

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

还有什么我需要做的吗?

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

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

bjl*_*aub 27

boost::asio::ip::tcp::socket每次重新连接时都需要创建一个新的.执行此操作的最简单方法可能是使用a在堆上分配套接字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);
    // ...
}
Run Code Online (Sandbox Code Playgroud)

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

void MyClient::mydisconnect(void)
{
    // ...
    // deallocate socket.  will close any open descriptors
    socket.reset();
}
Run Code Online (Sandbox Code Playgroud)

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

  • @Anonymous这是 std::shared_ptr 的一种方法,用参数替换托管对象:http://en.cppreference.com/w/cpp/memory/shared_ptr/reset (2认同)

Gra*_*amS 8

为了清楚起见,这是我使用的最终方法(但这是基于bjlaub的答案,所以请给他任何赞成):

我宣布该socket成员为scoped_ptr:

boost::scoped_ptr<boost::asio::ip::tcp::socket> socket;
Run Code Online (Sandbox Code Playgroud)

然后我修改了我的connect方法:

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

    // Create new socket (old one is destroyed automatically)
    socket.reset(new boost::asio::ip::tcp::socket(io_service));

    // Attempt connection
    socket->connect(server_endpoint, errorcode);

    if (errorcode)
    {
        cerr << "Connection failed: " << errorcode.message() << endl;
        socket->close();
    }
    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)
}
Run Code Online (Sandbox Code Playgroud)

注意:这个问题最初是在2010年提出并回答的,但是如果你现在使用的是C++ 11或更高版本,那么std::unique_ptr通常会比boost::scoped_ptr

  • @Ivan_Bereziuk:我编辑了我的答案。它最初发布于 2010 年,当时 C++11 尚未广泛使用。 (2认同)