[ASIO] Streambuf not containing full line
I am using asio asynchronically. I have: void tcp_session::start_read() { // Set a deadline for the read operation. // input_deadline_.expires_from_now(boost::posix_time::seconds(30)); // input_deadline_.expires_at(boost::posix_time::pos_infin); // Start an asynchronous operation to read a newline-delimited message. boost::asio::async_read_until(socket(), input_buffer_, '\n', boost::bind(&tcp_session::handle_read, shared_from_this(), boost::asio::placeholders::error)); } void tcp_session::handle_read(const boost::system::error_code& ec) { if (stopped()) return; if (!ec) { // Extract the newline-delimited message from the buffer. std::string msg; std::istream is(&input_buffer_); is.exceptions( std::ifstream::failbit | std::ifstream::badbit | std::ifstream::eofbit ); try { while ( std::getline(is, msg) ) { // parse msg here (gets truncated to last 256 chars or so) std::cerr << msg << std::endl; } } catch ( const std::ios_base::failure& e ) { std::cerr << "getline failure " << e.what() << std::endl; } } My problem is that I am seeing messages that are not a full line, but a truncation of the line, as if a buffer gets full with 256 characters. I am unsure what I am doing wrong. I get a getline failure saying basic_ios::clear.
On 27/02/2014 08:55, Quoth Gonzalo Garramuno:
I am using asio asynchronically. I have:
void tcp_session::start_read() { // Set a deadline for the read operation.
// input_deadline_.expires_from_now(boost::posix_time::seconds(30)); // input_deadline_.expires_at(boost::posix_time::pos_infin);
// Start an asynchronous operation to read a newline-delimited message. boost::asio::async_read_until(socket(), input_buffer_, '\n', boost::bind(&tcp_session::handle_read, shared_from_this(), boost::asio::placeholders::error)); }
Are you using the same (unmodified) buffer each time? async_read_until will pull more than a single line of data into the given buffer and expects the remaining data to remain in there for the next call.
std::string msg; std::istream is(&input_buffer_); is.exceptions( std::ifstream::failbit | std::ifstream::badbit | std::ifstream::eofbit );
try { while ( std::getline(is, msg) )
This is the wrong way to do it. This will eat more than the one line "officially" returned from the read call, including the still-being-received data at the end. It's also a waste of time to call "getline" again because you're just going to make it search for a newline that has already been found by async_read_until. A better way to do it is to include boost::asio::placeholders::bytes_transferred in your handler callback (as the "size" parameter), which then lets you simply: std::string line(boost::asio::buffer_cast<const char *>(input_buffer_.data()), size); input_buffer_.consume(size); (I believe this leaves the delimiter at the end though, so you might want to trim that off from your line too; but don't forget to still consume it.) Don't call it in a loop; just do it once and then issue the async_read_until again. This will complete immediately if the buffer already contains another line. A bit more complexity is required if you are mixing both async_read and async_read_until calls on the same socket.
participants (2)
-
Gavin Lambert
-
Gonzalo Garramuno