Boost logo

Boost :

Subject: [boost] [Asio] Match condition based on asio::streambuf length
From: Chet S (cstuut.boost_at_[hidden])
Date: 2008-10-27 17:50:46


Hello, I'm attempting to write a TCP-based server that can interpret a
protocol that has a prepended length followed by variable length data.
 To do this, I'm using async_read_until along with an asio::streambuf.
 In order to ensure that there is enough data in the buffer for my
next async read handler in the chain, I've written a match condition
that will return true if there is enough data already in the streambuf
to satisfy the read:

class read_until_length
{
public:
 explicit read_until_length(size_t len) : m_len(len) { }

 template<typename Iterator>
 std::pair<Iterator, bool> operator()(Iterator begin, Iterator end) const
 {
   Iterator i = begin;
   const size_t buffer_len = std::distance(begin,end);

   if(buffer_len >= m_len)
   {
     std::advance(i, m_len);
     return std::make_pair(i, true);
   }
   else
     return std::make_pair(end, false);
 }

private:
 size_t m_len;
};

And then I use it in chained calls to async_read_until as such:

Connection::start()
{
 // Call our next handler when we have received at least 4 bytes
(which represents the payload length that will follow)
 boost::async_read_until(m_socket, // boost::asio::ip::tcp::socket
                         m_data, // boost::asio::streambuf
                         read_until_length(4),
                         boost::bind(&Connection::read_element,
                                     this,
                                     boost::asio::placeholders::error));
}

Connection::read_length(boost::system::error_code& error)
{
 // Read out the 4 byte size header
 size_t length = 0;
 memcpy(&length, &*boost::asio::buffers_begin(m_data.data()), sizeof(size_t));
 m_data.consume(sizeof(size_t));

 // Read out the variable length data next now that we know the size of it
 boost::asio::async_read_until(m_socket,
                               m_data,
                               read_until_length(length),
                               boost::bind(&Connection::read_data
                                           this,
                                           length,
                                           boost::asio::placeholders::error));
}

Connection::read_data(size_t length, boost::system::error_code& error)
{
 // Consume the payload of length bytes...
}

The problem that I'm having is that async_read_until() performs two
checks on the match condition. The first
(boost/asio/impl/read_until.ipp:954) is to see if the iterator
returned from the match condition is at the end of the buffer. If
that is true, it does not post the next handler and assumes that not
enough data has been read from the socket. This causes a problem when
there is exactly enough data in the streambuf with the match condition
as written above.

It seems that returning true from the match condition (in the second
part of the std::pair<>) should still post a handler since the
condition was matched and it shouldn't be overridden by the iterator
being at the end of the buffer.

Is there a way to use async_read_until and a custom match condition to
match the exact size of the streambuf?

Thanks in advance.

--Chet


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk