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
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!
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
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.
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
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.