Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to keep a boost asio async read active continuously

I'm building a tcp client that must send requests and read responses, but also must be able to detect incoming data from the tcp server that is not a response to a request - the server can originate a tx/rx sequence.

What is the best way to keep an async read active all the time. I tried the following:

In my "handle_connect" method I start an async read and also an async write. The async read looks like this:


    size_t bytes_transferred = BUFFER_SIZE;
    boost::asio::async_read(m_socket,
        boost::asio::buffer(rcvbuf, bytes_transferred),
        boost::bind(&CClientSock::handle_read, 
        this,
        boost::asio::placeholders::error,
        boost::asio::placeholders::bytes_transferred));

The async write fills a buffer and then starts the write:


    boost::asio::async_write(m_socket,
        boost::asio::buffer(sndbuf, request_length),
        boost::bind(&CClientSock::handle_write, 
        this,
        boost::asio::placeholders::error,
        boost::asio::placeholders::bytes_transferred));

In the async write handler - it does nothing since the async read is already started.

This doesn't work. The data that should be read from the server's response to the client's write never triggers the async read handler. I'm not actually certain the write ever executes...

Can this be done since the reads and writes are not queued in the normal order? Would I have to start a read, then cancel it if I needed to start a write?

like image 906
Ken Avatar asked Sep 18 '14 12:09

Ken


2 Answers

In absense of a clear SSCCE/sample, I'll highlight a few ideas:

  • to accept "unsollicited" incoming data/requests, you typically use a listener to accept new incoming connections as opposed to awaiting data indefinitely on the same connection
  • if you need to sequence different asynchronous tasks on the same resource, use a Boost Asio strand: Why do I need strand per connection when using boost::asio?
  • to keep reading, in Actor Based asynchrony, just post the read operation from within the completion handler for the previous read operation:

     void handle_read(boost::system::error_code ec, size_t bytes_received) {
         if (!ec)
         {
    
             /* do your usual handling on the incoming data */
    
    
             boost::asio::async_read(m_socket,
                boost::asio::buffer(rcvbuf, bytes_transferred),
                boost::bind(&CClientSock::handle_read, 
                this,
                boost::asio::placeholders::error,
                boost::asio::placeholders::bytes_transferred));
         }
    
     }
    
like image 116
sehe Avatar answered Oct 18 '22 00:10

sehe


Well, I have the answer to my initial question, but sehe brought up the possibility of using strands so I may not have a complete understanding.

My initial problem was the result of restructuring code and not realizing my earlier code worked only under the circumstances that I tested against. I now have this working using the following sequence:

  • Issue an async_connect.
  • In the connect_handler issue an async_read_some - this was originally an async_read which never completed because of the number of bytes available. This was the reason for the Stack Overflow question.
  • In the connect_handler also issue an async_write.
  • In the write_handler do nothing since a read is already queued.
  • The write triggers a response from the server and the read_handler is called correctly.
  • The read_handler processes the data and queues another async_read_some.

This sequence seems to be repeatable indefinitely without error.

Regarding strands - the code is multi-threaded so there are multiple threads and each thread may be managing multiple socket connections. There are no socket calls across threads - that is, a socket is never acted on by code other than in the thread where the socket was created. However, there is only one io_service object for the project, which seems to be the recommended way to use the io_service object in a multi-thread application.

Am I correct that strands are not necessary here?

like image 25
Ken Avatar answered Oct 17 '22 23:10

Ken