[asio] Closing async client socket after writing last message
Hi, I have adapted the chat tutorial example to send messages to a similar (chat tutorial based) server. It is currently working fine however due to the async_write(), I can't close the socket until after all the pending data has been processed on the server end. I have google and found discussion and mentioned about async_write() completion handler calling socket.close() however, I was not able to find specific detail as to how that is done. I want to avoid closing the connection prematurely as it is asynchronous writing. My current do_write() looks like this void DisplayChatClient::do_write(DisplayChatMessage msg) { DLOG(INFO) << "DisplayChatClient::do_write()"; bool write_in_progress = !write_msgs_.empty(); write_msgs_.push_back(msg); if (!write_in_progress) { boost::asio::async_write(socket_, boost::asio::buffer(write_msgs_.front().data(), write_msgs_.front().length()), boost::bind(&DisplayChatClient::handle_write, this, boost::asio::placeholders::error)); } } void DisplayChatClient::handle_write(const boost::system::error_code& error) { DLOG(INFO) << "DisplayChatClient::handle_write()"; if (!error) { write_msgs_.pop_front(); if (!write_msgs_.empty()) { boost::asio::async_write(socket_, boost::asio::buffer(write_msgs_.front().data(), write_msgs_.front().length()), boost::bind(&DisplayChatClient::handle_write, this, boost::asio::placeholders::error)); } } else { do_close(); } } Cheers -- Nicholas Yue Graphics - Arnold, Alembic, RenderMan, OpenGL, HDF5 Custom Dev - C++ porting, OSX, Linux, Windows http://au.linkedin.com/in/nicholasyue https://vimeo.com/channels/naiadtools
On 18 June 2014 12:59, Bjorn Reese
On 06/18/2014 08:48 PM, Nicholas Yue wrote:
write_msgs_.pop_front();
if (!write_msgs_.empty())
Swap these two lines; otherwise you do not write the last message.
Thanks for the heads up. Does that mean that the examples will example this problem ? http://www.boost.org/doc/libs/1_55_0/doc/html/boost_asio/example/cpp03/chat/... as it does a pop before checking Cheers -- Nicholas Yue Graphics - Arnold, Alembic, RenderMan, OpenGL, HDF5 Custom Dev - C++ porting, OSX, Linux, Windows http://au.linkedin.com/in/nicholasyue https://vimeo.com/channels/naiadtools
On Wed, Jun 18, 2014 at 4:49 PM, Nicholas Yue
On 18 June 2014 12:59, Bjorn Reese
wrote: On 06/18/2014 08:48 PM, Nicholas Yue wrote:
write_msgs_.pop_front();
if (!write_msgs_.empty())
Swap these two lines; otherwise you do not write the last message.
Thanks for the heads up.
Does that mean that the examples will example this problem ?
http://www.boost.org/doc/libs/1_55_0/doc/html/boost_asio/example/cpp03/chat/...
as it does a pop before checking
Cheers -- Nicholas Yue
AFAIK this isn't a bug in the example code or in your adaption. The implementation does a push_back() before the async_write request, and does a pop_front() when it completes. So its using the std::deque to store the contents of the message during the async operation. The .empty() check is done in the async_write callback in case multiple messages were queued while an async_write was still pending. As for your original question - information was a little sparse, so I will guess that you want the client to write out all of the queued messages and then initiate the socket close. You need a mechanism for delaying a close request until a future point in time. The close() method will have to "post" another method (safe_close perhaps) that only closes the connection if the queue is empty - otherwise safe_close starts yet-another-event to be handled in the future. This other event can be an async timer or an immediate change to a flag that handle_write reads when the queue is empty. I'm not aware of any other implementations. Both of my suggested implementations (timer or flag) have drawbacks, so the particular application has to be considered. Lee
On 06/18/2014 10:49 PM, Nicholas Yue wrote:
Does that mean that the examples will example this problem ?
http://www.boost.org/doc/libs/1_55_0/doc/html/boost_asio/example/cpp03/chat/...
as it does a pop before checking
Sorry, I read the code too quickly. So if I understand your question correctly, the chat example will discard queued messages on a close, whereas you want the close to be deferred until the queue is empty? One way to achieve this is to change do_close() to check the queue. If empty, close the socket, and if not then set a want_to_close flag. Then change handle_write() to check the want_to_close flag if the queue is empty, and call do_close().
On 19/06/2014 06:48, Nicholas Yue wrote:
I have adapted the chat tutorial example to send messages to a similar (chat tutorial based) server.
It is currently working fine however due to the async_write(), I can't close the socket until after all the pending data has been processed on the server end.
I have google and found discussion and mentioned about async_write() completion handler calling socket.close() however, I was not able to find specific detail as to how that is done.
Do you want something like an actual chat server where connection close can occur at any arbitrary time unrelated to data transmission, or something more like HTTP/FTP where you know in advance that you want to close the connection immediately following a particular transmit? Because these two patterns have different solutions, and the ones being discussed in this thread so far are mostly the former while the one you mentioned in the last quoted paragraph above is for the latter (and is much simpler).
On 22/06/14 6:58 PM, Gavin Lambert wrote:
On 19/06/2014 06:48, Nicholas Yue wrote:
I have adapted the chat tutorial example to send messages to a similar (chat tutorial based) server.
It is currently working fine however due to the async_write(), I can't close the socket until after all the pending data has been processed on the server end.
I have google and found discussion and mentioned about async_write() completion handler calling socket.close() however, I was not able to find specific detail as to how that is done.
Do you want something like an actual chat server where connection close can occur at any arbitrary time unrelated to data transmission, or something more like HTTP/FTP where you know in advance that you want to close the connection immediately following a particular transmit?
Because these two patterns have different solutions, and the ones being discussed in this thread so far are mostly the former while the one you mentioned in the last quoted paragraph above is for the latter (and is much simpler). I am after the latter.
Currently, I call a different handler when I am sending the last message. That handler does a close. My tests so far indicates that it does the close after the last message was sent. I would like to hear alternative solution as my approach may not be technically correct and may have problem during high load. Cheers -- Nicholas Yue Graphics - RenderMan, Visualization, OpenGL, HDF5 Custom Dev - C++ porting, OSX, Linux, Windows http://au.linkedin.com/in/nicholasyue https://vimeo.com/channels/naiadtools
On 23/06/2014 14:25, Nicholas Yue wrote:
On 22/06/14 6:58 PM, Gavin Lambert wrote:
Do you want something like an actual chat server where connection close can occur at any arbitrary time unrelated to data transmission, or something more like HTTP/FTP where you know in advance that you want to close the connection immediately following a particular transmit? [...] I am after the latter.
Currently, I call a different handler when I am sending the last message. That handler does a close. My tests so far indicates that it does the close after the last message was sent.
That seems like a reasonable solution to me. (Others include using the same handler but checking a flag or queue that "knows" when the last thing has been sent. But using a separate handler will be the fastest of those.) Although the notes for close() do mention that it would be safest to call shutdown() first. Otherwise there is presumably still a chance that the OS has successfully queued but not actually transmitted the data, and closing the socket might still cancel it.
I would like to hear alternative solution as my approach may not be technically correct and may have problem during high load.
What sort of problems are you anticipating? (There's no substitute for testing, of course.)
participants (4)
-
Bjorn Reese
-
Gavin Lambert
-
Lee Clagett
-
Nicholas Yue