16

Suppose I have a socket:

std::shared_ptr<tcp::socket> socket( new tcp::socket(acceptor.get_io_service()) );
acceptor.async_accept( *socket, std::bind( handleAccept, this, std::placeholders::_1, socket, std::ref(acceptor)) );

And I store a weak_ptr to the said socket in a container. I need this because I want to allow clients to request for a list of other clients, so they can send messages to each other.

clients_.insert(socket);  // pseudocode

Then I run some async operations

socket->async_receive( boost::asio::buffer(&(*header), sizeof(Header))
    , 0
    , std::bind(handleReceiveHeader, this, std::placeholders::_1, std::placeholders::_2, header, socket));

How do I detect when the connection is closed so I can remove my socket from the container?

clients_.erase(socket); // pseudocode
aCuria
  • 6,935
  • 14
  • 53
  • 89

2 Answers2

25

A TCP socket disconnect is usually signalled in asio by an eof or a connection_reset. E.g.

  void async_receive(boost::system::error_code const& error,
                     size_t bytes_transferred)
  {
    if ((boost::asio::error::eof == error) ||
        (boost::asio::error::connection_reset == error))
    {
      // handle the disconnect.
    }
    else
    {
       // read the data 
    }
  }

I use boost::signals2 to signal the disconnect although you can always pass a pointer to a function to your socket class and then call that.

Be careful about your socket and callback lifetimes, see: boost-async-functions-and-shared-ptrs

Community
  • 1
  • 1
kenba
  • 4,303
  • 1
  • 23
  • 40
  • 2
    A list of errors can be found here: http://www.boost.org/doc/libs/1_44_0/doc/html/boost_asio/reference/error__basic_errors.html – aCuria Nov 12 '13 at 03:31
  • I have found that this indeed works, I get the error "connection_reset". However, in the documentation I notice another error code, "connection_aborted". Is it necessary to handle this error code as well? – aCuria Nov 12 '13 at 03:35
  • That's a useful link @aCuria. Another source of error codes is the file `boost/system/error_code.hpp`. As for the "connection_aborted" error, I haven't seen it in my applications, but there's no harm in testing for it as well. – kenba Nov 12 '13 at 21:14
  • Thank you, it was usefull, but I wonder, why in a simple boost::asio::ssl [client-server application] (http://www.boost.org/doc/libs/1_37_0/doc/html/boost_asio/example/ssl/server.cpp), when I send `kill -9 ` I am getting directly to `session::~session()` but not into the `session::handle_read(...)` function which corresponds to your example. – Vitaly Isaev Feb 14 '14 at 14:31
  • 4
    Cool. But what if you don't want to receive any data? The only thing I want to know is whether the connection is up or down, without even trying to read anything remote side sends. Looks like there is no such API in asio. – Oleg Andriyanov Dec 17 '15 at 18:07
  • 2
    @OlegAndriyanov your requirement sounds quite unusual. It's a communications protocol, so it sending and receiving data is it's raison d'etre... – kenba Dec 18 '15 at 10:40
  • @aCuria The "connection_aborted" error occurs only when it is enabled, and by default it is disabled. See https://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/basic_socket_acceptor/enable_connection_aborted.html – Ian Gralinski Jul 02 '19 at 00:29
1

There are many options, some of them are:

  1. As you store weak_ptr in container - it will not prolong lifetime of socket, so when your handler will get boost::asio::error::eof (or whatever), it will not do copy/move of shared_ptr, and socket will be deleted (if you don't have any others shared_ptrs to it). So, you can do something like: if(socket.expired()) clients_.erase(socket);

  2. Check error code in your handler - it will indicate when connection is closed. Using this info - you can call clients_.erase from handler itself.

Could you give an example of #2?

It will be something like:

socket->async_receive
(
    boost::asio::buffer(&(*header), sizeof(Header)), 0,
    [=, &clients_](const system::error_code& error, size_t bytes_transferred)
    {
        if(error) // or some specific code
        {
            clients_.erase(socket); // pseudocode
        }
        else
        {
            // continue, launch new operation, etc
        }
    }
);
Evgeny Panasyuk
  • 9,076
  • 1
  • 33
  • 54