boost::asio::async_read_until fills streambuf less than actually received
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
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).
participants (2)
-
David Demelier
-
Gavin Lambert