简体   繁体   中英

Force asynchronous socket read to finish early using Boost.Asio

I have a tcp::socket for reading and writing data. There is a read loop made up by chaining async_read_some() and the handler on_data_read() that calls async_read_some() again after dealing with the data read. A relevant shared_ptr<tcp::socket> is passed along the loop. The loop ends when on_data_read() is called with a non-success error_code such as asio::error::eof , in which case async_read_some() is not called.

There may also be asynchronous writes on the socket, which is done by repeated calls to async_write_some() until all data is written. That is, the handler on_data_written() for async_write_some() calls async_write_some() again if the data is only partially written. The relevant shared_ptr<tcp::socket> is also passed along the call-chain.

Now, I want to close the socket in a safe manner. Specifically, I want to force the asynchronous read to finish early with a non-success error code to ends the read loop. If there is no pending write call-chain, the only shared_ptr<tcp::socket> (the one passed along the read loop) gets destroyed with its managed tcp::socket closed and destroyed. If there is a pending write call-chain, it continues until all data is written. At the time the write call-chain goes to its end, the last shared_ptr<tcp::socket> (the one passed along the write call-chain) gets destroyed. This procedure is safe in the sense that the socket is closed after pending writes, if any.

The problem is

  • how can I force the asynchronous socket read to finish with a non-success error code?

I've checked the linger option. But it won't work since I'm using chained-up async_write_some() instead of a single async_write() . So, the socket may be closed while on_data_written() is being called. cancel() and close() won't work either, since they interrupt not only the read loop but also the write call-chain. And although shutdown() can be applied to the read loop only, it prevents future async_read_some() calls only, and has no effect on what is already done. I've worked out a workaround solution. That is, call cancel() , but have on_data_written() ignore the the error code caused by cancel() and continue the write call-chain. I'm not satisfied with this solution (see the remarks section here ). I'm wondering if there is a more direct and elegant way to achieve what I want, or the whole design is just flawed?

In my opinion you've summed it up pretty nicely.

You cannot really do better than fullblown cancel. Indeed you may resume any canceled writes.

I don't think there is anything more elegant. I would not say the design is flawed, but you might want to consider not actually canceling pending operations, but instead just keeping a flag to indicate whether a "logical read cancel" is pending and prevent chaining more reads in that case

When you used shutdown.. eg.. socket.shutdown did you use the shutdown_both?

(boost::asio::ip::tcp::socket::shutdown_both, ec)

The shutdown_both option. That should handle read and write closures.

socket.shutdown(boost::asio::ip::tcp::socket::shutdown_both, errorcode);
if (errorcode)
{
    cerr << "socket.shutdown error: " << errorcode.message() << endl;
}

Also.. If you have the io_service handle. you can call io_service.stop() as a last resort which will shutdown all operations.

Shutdown the socket for input. That will cause all pending reads to encounter end of stream and return accordingly.

And although shutdown() can be applied to the read loop only, it prevents future async_read_some() calls only, and has no effect on what is already done.

I don't know where you got this misinformation from. I don't even know what it means. Shutdown applies to the socket, not a read loop.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM