Search code examples
c++boost-asiostreambuf

asio::async_read_until: robust and graceful way of handling multiple lines


I'm using asio::async_read_until with '\n' delimiter to support a TCP client that fetches character data from a server. This server continuously sends '\n' terminated lines; precisely, it can write at once either single lines or a concatenated string of multiple lines.

From the doc, I understand that asio::async_read_until could read:

  • One '\n' terminated line, like "some_data\n". This is the simplest case, handled with a call the std::getline on the stream associated with the asio::streambuf
  • One '\n' terminated line plus the beginning of a next line, like "some_data1\nbla". This can be handled with a std::getline; the rest of the second line will be handled at the next completion handler call.
  • Many lines; in this case, the newly read data could contain 2 or more '\n'. How can I know how many std::getline calls I should do, knowing that I don't want to risk calling std::getline on an incomplete line (which I will eventually get in a future packet)? Should I peek at the stream buffer to check the existence of multiple '\n'? Is it even possible without doing many copies?

Solution

  • from the documentation here:

    http://www.boost.org/doc/libs/1_59_0/doc/html/boost_asio/reference/async_read_until/overload1.html

    If the stream buffer already contains a newline, the handler will be invoked without an async_read_some operation being executed on the stream.

    For this reason, when your handler executes you must execute no more than one getline(). Once getline has returned and you have finished processing, simply call async_read_until again from the handler.

    example:

    void handler(const boost::system::error_code& e, std::size_t size)
    {
      if (e)
      {
        // handle error here
      }
      else
      {
        std::istream is(&b);
        std::string line; 
        std::getline(is, line);
        do_something(line)
        boost::asio::async_read_until(s, b, '\n', handler);
      }
    }
    
    // Call the async read operation
    boost::asio::async_read_until(s, b, '\n', handler);