I'm writing a server with boost's asio library. The server handles many concurrent connections using a collection of Connection objects (a wrapper class around boost::asio::tcp::socket). Within the Connection class, the socket is constantly being read from using socket.async_read_some(...) and whenever the read handler is invoked with new data, socket.async_read_some() is immediately called again for more data to be read.
Now, the server may decide to disconnect a client for some reason, so the natural thing to do is to call connection.close() which in turn calls socket.close(), which will cause all pending async operation to be canceled. This causes the read handler (bound to a method within class Connection) to be invoked with boost::asio::error::operation_aborted. And my problem is: I don't want this to happen.
After socket.close(), I'd like to destroy the socket and the connection and then remove its pointer from the server's list of active clients. However the read handler won't be called until the next iteration of io_service.run(), which means that I cannot immediately destroy the socket or the read handler I had passed to socket.async_read_some() until the handler has been invoked with the error. So I have to delay the destruction of those objects somehow; that's annoying.
Is there a safe way to either
Or am I approaching this entirely the wrong way?
When an async.operation is complete - either successfuly or with error - its completion handler is invoked. This is important guarantee, and I don't think it's good idea to try and "hack" this behavior.
The problem you encountered in your use-case is usually sovled by using shared_ptr (shared_from_this idiom): bind shared_ptr<Connection>
to the handlers, don't issue another async_read when you get operation_aborted (or some other error), so that when all the handlers are done the Connection object is destroyed with its socket.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With