boost::asio::async_read_until fills streambuf less than actually received

classic Classic list List threaded Threaded
2 messages Options
Reply | Threaded
Open this post in threaded view
|

boost::asio::async_read_until fills streambuf less than actually received

Boost - Users mailing list
Hello,

I'm using Asio to read IRC messages asynchronously from a server. I use
boost::asio::async_read_until with "\r\n" as delimiter and a
boost::asio::streambuf as output buffer.

I mostly implemented the code like described on my original
question/answer here:

https://stackoverflow.com/questions/40561097/read-until-a-string-delimi
ter-in-boostasiostreambuf

I sometimes get the bytes_transferred argument bigger that what is
filled into the buffer.

My code looks like this:

    void connection::do_recv(handler_t handler)
    {
        boost::asio::async_read_until(socket_, buffer_, "\r\n", [this,
handler] (auto code, auto xfer) {
            if (code || xfer == 0U)
                handler(std::move(code), message());
            else {
                //assert(buffer_.size() > xfer);
   
                std::cout << "xfer:    " << xfer << std::endl;
                std::cout << "size:    " << buffer_.size() <<
std::endl;
   
                std::string str(
                    boost::asio::buffers_begin(buffer_.data()),
                    boost::asio::buffers_begin(buffer_.data()) + xfer -
2
                );
   
                buffer_.consume(xfer);
                handler(std::move(code), message::parse(str));
            }
        });
    }
   
    void connection::rflush()
    {
        if (input_.empty())
            return;
   
        do_recv([this] (auto code, auto message) {
            auto h = input_.front();
   
            // Delete first, in case of exceptions.
            input_.pop_front();
   
            if (h)
                h(code, std::move(message));
            if (!code)
                rflush();
        });
    }
   
    void connection::recv(handler_t handler)
    {
        auto in_progress = !input_.empty();
   
        input_.push_back(std::move(handler));
   
        if (!in_progress)
            rflush();
    }

For some reasons, after several calls, I get an assertion failure in
the std::string construction. Because buffer_.size() is actually
smaller than xfer. I could not understand why.

Example of output:

...
...
xfer:    87                                  
size:    141

Note: I've implemented the recv() public function like what I can see
for send in some tutorials. I wanted a queue of handlers so I can
safely call recv() multiple times before first functions finished
completely.

Regards,

--
David
_______________________________________________
Boost-users mailing list
[hidden email]
https://lists.boost.org/mailman/listinfo.cgi/boost-users
Reply | Threaded
Open this post in threaded view
|

Re: boost::asio::async_read_until fills streambuf less than actually received

Boost - Users mailing list
On 2/04/2018 20:46, David Demelier wrote:

> I'm using Asio to read IRC messages asynchronously from a server. I use
> boost::asio::async_read_until with "\r\n" as delimiter and a
> boost::asio::streambuf as output buffer.
>
> I mostly implemented the code like described on my original
> question/answer here:
>
> https://stackoverflow.com/questions/40561097/read-until-a-string-delimi
> ter-in-boostasiostreambuf
>
> I sometimes get the bytes_transferred argument bigger that what is
> filled into the buffer.

I haven't looked at your code, but the most common problem with using
[async_]read_until is correctly preserving the streambuf between calls
(and realising that you need to do so).

The way that read_until works under the hood is that it reads an
arbitrary amount of data from the underlying socket into the streambuf,
and then returns a value *less than* the amount of data it actually read
to indicate at which point the delimiter was found.

You are required to preserve the same streambuf and pass it to the next
call (which must also be an [async_]read_until call -- don't alternate).
  This call will *first* search the existing data in the streambuf for
your next delimiter and may return immediately with a value less than
the amount of data remaining in the streambuf, without actually
performing another network read.  Or it may again append an arbitrarily
large block of additional data and then return a subset of it up to the
delimiter.

If you can't avoid alternating read_untils with other kinds of reads,
then it is your responsibility to check if the streambuf already
contains sufficient data to satisfy your non-until read, and only issue
an actual read request for the balance (if any).

_______________________________________________
Boost-users mailing list
[hidden email]
https://lists.boost.org/mailman/listinfo.cgi/boost-users