c++network-programmingboostboost-asio

How to use async_read_some after async_read_until with same buffer?


I need to do async_read_until, after which I want to call async_read_some using the same boost::asio::streambuf that was used for async_read_until.

The reason why I want to use the same boost::asio::streambuf is that after async_read_until there might be some data left in the buffer. When reading via async_read_some I want to get not only the new data that is available but also the data that might have been left after async_read_until.

However, async_read_some does not have an overload for boost::asio::streambuf. How can I solve this problem?

Here is an example of the code I need to execute. Note that read_some can be called multiple times.

class io
{
    //...
public:

    void read_until()
    {
        boost::asio::async_read_until(m_socket, m_buffer, "\n", 
            [this](const boost::system::error_code& ec, std::size_t bytes_transferred)
        {
            read_some();
        });
    }

    void read_some()
    {
        // I need use async_read_some here into m_buffer
        // ...
        // read_some() can be called again from completion handler
    }

private:
    boost::asio::streambuf m_buffer;
    boost::asio::ip::tcp::socket m_socket;
};

I read that you can use prepare/commit/consume, but I don't fully understand how to use these methods correctly. Can someone give an example and explain what exactly these methods do and how to use them correctly?


UPDATE

Please answer the following questions:

  1. Is it true that boost::asio::streambuf consists of at least two buffers? A read buffer and a write buffer?
  2. If so, is it true that boost::asio::streambuf::prepare allocates space in the write buffer?
  3. If so, is it true that boost::asio::streambuf::commit copies data from the write buffer to the read buffer?
  4. Is it true that boost::asio::streambuf::consume removes data from the read buffer?
  5. Is it true that if the read buffer contained data [my_data], and then prepare(n), async_read_some and commit is called, then after commit the read buffer will contain [my_data]+[new_data_from_async_read_some]?

Solution

    1. Q. Is it true that asio::streambuf consists of at least two buffers? A read buffer and a write buffer?

      Not necessarily. It might be, or it might not be. asio::streambuf bridges two worls:

      • the Asio concept DynamicBuffer (v1) which defines the prepare, commit, and consume interface in terms of single buffers.

      • the iostreams std::streambuf which similarly describes an interface like¹

        enter image description here

        Note, here too there's a logical "input" and "output area" which can be from the same buffer (as in e.g. std::basic_stringbuf or std::basic_spanbuf

      That said, currently Asio's streambuf happens to always keep a single buffer in the storage model: is it safe to use boost::asio::streambuf as both an istream and an array as string_view?


    2. Q. If so, is it true that prepare allocates space in the write buffer?

      It reserves enough space for the output area. If necessary, it will (re)allocate.


    3. Q. If so, is it true that commit copies data from the write buffer to the read buffer?

      No. It merely manipulates some pointers to indicate the new extents of available input/output areas.


    4. Q. *Is it true that consume removes data from the read buffer?

      It will typically again just update some pointers, though implementations might shrink the allocated storage to fit. You can infer that implementations have this leeway from the invalidation specifications in the concept:

      expression assertion/note
      pre/post conditions
      x.consume(n) Removes n bytes from beginning of the input sequence. If n is greater than the size of the input sequence, the entire input sequence is removed. All constant or mutable buffer sequences previously obtained using data() or prepare() are invalidated.

    5. Q. Is it true that if the read buffer contained data "my_data", and then prepare(n), async_read_some and commit are called, then after commit the read buffer will contain "my_data"+[new_data_from_async_read_some]?

      Logically, yes. To be more precisely, you might refer to "get area" instead of "read buffer", matching the iostreams jargon, and avoiding the implication that data is copied during commit.


    ¹ image originally from https://cppreference.com/w/cpp/io/basic_streambuf.html where it is currently broken for some reason?