Reputation: 4505
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
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