Ghita
Ghita

Reputation: 4505

boost::asio - know when the conection has to be shutdown/closed

I implement a protocol (socks) that requires that my server to relay connections coming from the client to the destination.

The way I implement relaying part is by using something like this:

socket_.async_read_some(boost::asio::buffer(dataClient_, 1024),
boost::bind(&ProxySocksSession::HandleClientProxyRead, this,
 boost::asio::placeholders::error,
 boost::asio::placeholders::bytes_transferred));

remoteSock_.async_read_some(boost::asio::buffer(dataRemote_, 1024),
boost::bind(&ProxySocksSession::HandleRemoteProxyRead, this,
 boost::asio::placeholders::error,
 boost::asio::placeholders::bytes_transferred));

Of course there is more code - there are the handlers there that relay data coming from socket_ and sending it to remoteSock_ and the other way around (all data coming from remoteSock_ is relayed to socket_)

I saw the async tcp server echo example (http://www.boost.org/doc/libs/1_38_0/doc/html/boost_asio/example/echo/async_tcp_echo_server.cpp) and there the logic when to shutdown the connection was simply to delete the connection object - delete this - (that closed the communication socket it owned) when a boost::system::error_code was received in the handler.

How am I supposed to handle this case myself ? This time I have data coming on 2 sockets and if I don't shutdown cleanly I might end up closing before all data was transmitted (e.g. socket from client side - socket_ - might close the connection but - remoteSock - could still be trying to send data).

EDIT I have updated my code to a point where if I detect that one of the sockets (remoteSock_ or socket_) read/write handlers reported an boost::system::error_code I do the following in order to shutdown the communication:

void ProxySocksSession::Shutdown()
{
    if (!shutdownInProgress_)
    {
        std::cout << "Shuting down ..." << std::endl;
        shutdownInProgress_ = true;
        remoteSock_.shutdown((boost::asio::ip::tcp::socket::shutdown_both));
        remoteSock_.close();
        socket_.shutdown((boost::asio::ip::tcp::socket::shutdown_both));
        socket_.close();
        parentConnection_.Shutdown();
    }
}

The problem is that even if I call shutdown() and close() on the sockets I still receive calls to the socket handlers (these are in the same class, ProxySocksSession). By the time these come my ProxySocksSession instance is already deleted (deletion is done by parentConnection_.Shutdown() from above)

Upvotes: 4

Views: 3933

Answers (1)

Ghita
Ghita

Reputation: 4505

I have managed to come up with a solution that works (doesn't cause the problems described). I also include bellow the skeleton for the handler functions in order to see the idea:

void ProxySocksSession::Start()
{
    socket_.async_read_some(boost::asio::buffer(dataClient_, 1024),
       boost::bind(&ProxySocksSession::HandleClientProxyRead, shared_from_this(),
           boost::asio::placeholders::error,
           boost::asio::placeholders::bytes_transferred));

    remoteSock_.async_read_some(boost::asio::buffer(dataRemote_, 1024),
           boost::bind(&ProxySocksSession::HandleRemoteProxyRead, shared_from_this(),
           boost::asio::placeholders::error,
           boost::asio::placeholders::bytes_transferred));
}

// received data from socks5 client - completion handler
void ProxySocksSession::HandleClientProxyRead(const boost::system::error_code& error,
      size_t bytes_transferred)
{
     if (!error && !this->shutdownInProgress_)
     {
         // relay data coming from client to remote endpoint -> async write to remoteSock_
         // async read some more data from socket_
      }
      else
      {
         Shutdown();
      }
 }

//received data from socks5 remote endpoint (socks5 client destination)
void ProxySocksSession::HandleRemoteProxyRead(const boost::system::error_code& error,
      size_t bytes_transferred)
{
     if (!error && !this->shutdownInProgress_)
     {
         // relay data coming from remote endpoint to client -> async write to socket__
         // async read some more data from remoteSock_
      }
      else
      {
         Shutdown();
      }
 }

void ProxySocksSession::Shutdown()
{
    if (!shutdownInProgress_)
    {
        std::cout << "Shuting down ..." << std::endl;
        shutdownInProgress_ = true;
        //remoteSock_.close(); -- no need as it is closed automatically as part of parentConnection_ shutdown/deletion
        //socket_.close(); -- no need as it is closed automatically as part of parentConnection_ shutdown/deletion
        parentConnection_.Shutdown();
    }
}

The key here was the fact that I used shared_from_this() when handing to bind the completion handlers. This way I made sure that deletion of ProxySocksSession instance was not done by parentConnection_ instance that had a shared_ptr to ProxySocksSession before all ProxySocksSession handlers were called while sockets were closing down.

Upvotes: 2

Related Questions