How do I cleanly reconnect a boost::socket following a disconnect?

前端 未结 5 555
[愿得一人]
[愿得一人] 2020-12-08 07:13

My client application uses a boost::asio::ip::tcp::socket to connect to a remote server. If the app loses connection to this server (e.g. due to the server cras

相关标签:
5条回答
  • 2020-12-08 07:54

    You need to create a new boost::asio::ip::tcp::socket each time you reconnect. The easiest way to do this is probably to just allocate the socket on the heap using a boost::shared_ptr (you could probably also get away with scoped_ptr if your socket is entirely encapsulated within a class). E.g.:

    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);
        // ...
    }
    

    Then, when mydisconnect is called, you could deallocate the socket:

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

    The error you're seeing is probably a result of the OS cleaning up the file descriptor after you've called close. When you call close and then try to connect on the same socket, you're probably trying to connect an invalid file descriptor. At this point you should see an error message starting with "Connection failed: ..." based on your logic, but you then call mydisconnect which is probably then attempting to call shutdown on an invalid file descriptor. Vicious cycle!

    0 讨论(0)
  • 2020-12-08 07:54

    Since C++11 you can write:

    decltype(socket)(std::move(socket));
    // reconnect socket.
    

    The above creates a local instance of socket's type move constructing it from socket.

    Before the next line, the unnamed local instance is destructed, leaving socket in a "freshly constructed from io_service" state.

    See: https://www.boost.org/doc/libs/1_63_0/doc/html/boost_asio/reference.html#boost_asio.reference.basic_stream_socket.basic_stream_socket.overload5

    0 讨论(0)
  • 2020-12-08 07:55

    I've done something similar using Boost.Asio in the past. I use the asynchronous methods, so a reconnect is typically letting my existing ip::tcp::socket object go out of scope, then creating a new one for calls to async_connect. If async_connect fails, I use a timer to sleep a bit then retry.

    0 讨论(0)
  • 2020-12-08 07:57

    For the sake of clarity here is the final approach I used (but this is based on bjlaub's answer, so please give any upvotes to him):

    I declared the socket member as a scoped_ptr:

    boost::scoped_ptr<boost::asio::ip::tcp::socket> socket;
    

    Then I modified my connect method to be:

    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)
    }
    

    Note: this question was originally asked and answered back in 2010, but if you are now using C++11 or later then std::unique_ptr would normally be a better choice than boost::scoped_ptr

    0 讨论(0)
  • 2020-12-08 08:04

    I have tried both the close() method and the shutdown method and they are just to tricky for me. Close() can throw an error that you need to catch and is the rude way to do what you want :) and shutdown() seems to be best but on multithreaded software, I find it can be fussy. So the best way is, as Sam said, to let it go out of scope. If the socket is a member of the class you can 1) redesign so that the class uses a 'connection' object to wrap the socket and let it go out of scope or 2) wrap it in a smart pointer and reset the smart pointer. If you using boost, including the shared_ptr is cheap and works like a charm. Never had a socket clean up issue doing it with a shared_ptr. Just my experience.

    0 讨论(0)
提交回复
热议问题