0

I'm having what doesn't seem to be a problem but I don't understand the error. Basically, I've been trying to work on code that "gracefully" shuts down a tcp socket on both ends. At the end of my program, on both sides, I shutdown my sockets before closing them. On a quick restart, which is the thing I'm trying to solve, the much worse problem of crashing because the sockets are lingering on both sides doesn't happen anymore. The shutdown/then close seems to work on that front.

However, i get "Address already in use" still, which usually made it so that I couldn't connect. Now I'm able to connect after that error just fine. I've read a lot on the subject of graceful shutdown, reuse address, and the like. And I guess my question is, how, if the socket error'd on bind ("Address already in use"), after a successful open, is it possibly able to connect to the endpoint? In other words, if the address is actually already in use, how is the connection being made? Also, of note, reuse address doesn't work in this situation. Because I'm using the same socket settings, local/remote addresses and ip.

Community
  • 1
  • 1
Ryan Bartley
  • 606
  • 8
  • 17
  • Are you quite sure the old process is really killed so you're connecting to the old one instead of the new one which gives the error? – Joachim Isaksson Feb 12 '16 at 15:12
  • If this is on linux/os x/freebsd you can try a netstat -na|grep your-port-number and see if the process is still hanging around. – kometen Feb 12 '16 at 15:15
  • Thanks for the comments. @Joachim, I'm pretty sure the process is dead. I've closed down the window completely and definitely hit shutdown and close and the socket destructor. That would be pretty amazing if the OS was somehow allowing a new process to take control of an old processes socket. Seems very not safe. kometen, I'll give it a try for sure – Ryan Bartley Feb 12 '16 at 21:56

1 Answers1

1

Failing to bind() the socket to an address does not invalidate the underlying socket. As such, the connect() operation will continue with an unbound socket, deferring to the kernel to bind to a local endpoint.


Here is a complete example demonstrating this behavior:

#include <boost/asio.hpp>
#include <boost/bind.hpp>

// This example is not interested in all handlers, so provide a noop function
// that will be passed to bind to meet the handler concept requirements.
void noop() {}

int main()
{
  using boost::asio::ip::tcp;

  // Create all I/O objects.
  boost::asio::io_service io_service;
  tcp::acceptor acceptor(io_service, {tcp::v4(), 0});
  tcp::socket server(io_service, tcp::v4());

  // Open socket1, binding to a random port.
  tcp::socket socket1(io_service, {boost::asio::ip::address_v4::any(), 0});
  tcp::socket socket2(io_service); // non-open

  // Explicitly open client2, which will bind it to the any address.
  boost::system::error_code error;  
  socket2.open(tcp::v4(), error);
  assert(!error);
  assert(socket2.local_endpoint().port() == 0);

  // Attempt to bind socket2 to socket1's address will fail with
  // an already in use error, leaving socket2 bound to the any endpoint.
  // (e.g. a failed bind does not have side effects on the socket) 
  socket2.bind(socket1.local_endpoint(), error);
  assert(error == boost::asio::error::address_in_use);
  assert(socket2.local_endpoint().port() == 0);

  // Connect will defer to the kernel to bind the socket.
  acceptor.async_accept(server, boost::bind(&noop));  
  socket2.async_connect(acceptor.local_endpoint(), 
    [&error](const boost::system::error_code& ec) { error = ec; });
  io_service.run();
  io_service.reset();
  assert(!error);
  assert(socket2.local_endpoint().port() != 0);
}
Tanner Sansbury
  • 51,153
  • 9
  • 112
  • 169