I'm having trouble implmenting the 3rd parameter in the function documented here: http://www.boost.org/doc/libs/1_39_0/doc/html/boost_asio/reference/async_read_until/overload4.html What I'd like to be able to do is use the callback on the 3rd parameter of async_read_until to detect when a complete chunk has arrived. My packets have the following format.
- 1 byte id (semantic meaning of the data)
- unsigned int (the number of bytes in the data, since some data chunks can change size)
- payload
Looking at the exam开发者_如何学编程ple code in the documentation, I'm a little confused about how I'm supposed to be able to extract a byte, let alone an unsigned int from the begin and end iterators.
I've instantiated my iterators as
typedef boost::asio::buffers_iterator<
boost::asio::streambuf::const_buffers_type> iterator;
but even then I'm not sure what type that is, since I don't know what const_buffers_type is. I followed some links in the documentation and found out it was "implementation defined", but I guess I could be wrong. So my two concrete questions are:
- how can I use those two iterators to read an unsigned int?
- what type are those iterators pointing to?
Thanks!
A sample match function is presented in the documentation.
std::pair<iterator, bool>
match_whitespace(iterator begin, iterator end)
{
iterator i = begin;
while (i != end)
if (std::isspace(*i++))
return std::make_pair(i, true);
return std::make_pair(i, false);
}
Dereferencing i
here, pulls out one byte. You need to pull out enough bytes to match an int.
Remember however, that a callback is not the only option for read_until. Actually it's the most complex. Are you sure that it wouldn't be enough to use a regex instead?
template<
typename AsyncReadStream,
typename Allocator,
typename ReadHandler>
void async_read_until(
AsyncReadStream & s,
boost::asio::basic_streambuf< Allocator > & b,
const boost::regex & expr,
ReadHandler handler);
Anyway, considering that your read is not delimeted, a lot better way would be to async_read_some until you've read the size, and then async_read_some with read at least.
I have a very similar message format to yours (16 bit payload length, 8bit packet id/type, followed by the payload for me). I did it with a 3 phase read, and an array of function pointers for handling different things. I used boost::asio::async_read to read known amounts at a time.
This is a simplified version of my code:
//call this to start reading a packet/message
void startRead(boost::asio::ip::tcp::socket &socket)
{
boost::uint8_t *header = new boost::uint8_t[3];
boost::asio::async_read(socket,boost::asio::buffer(header,3),
boost::bind(&handleReadHeader,&socket,header,
boost::asio::placeholders::bytes_transferred,boost::asio::placeholders::error));
}
void handleReadHeader(boost::asio::ip::tcp::socket *socket,
boost::uint8_t *header, size_t len, const boost::system::error_code& error)
{
if(error)
{
delete[] header;
handleReadError(error);
}
else
{
assert(len == 3);
boost::uint16_t payLoadLen = *((boost::uint16_t*)(header + 0));
boost::uint8_t type = *((boost::uint8_t*) (header + 2));
delete[] header;
//dont bother calling asio again if there is no payload
if(payLoadLen > 0)
{
boost::uint8_t *payLoad = new boost::uint8_t[payLoadLen];
boost::asio::async_read(*socket,boost::asio::buffer(payLoad,payLoadLen),
boost::bind(&handleReadBody,socket,
type,payLoad,payLoadLen,
boost::asio::placeholders::bytes_transferred,boost::asio::placeholders::error));
}
else handleReadBody(socket,type,0,0,0,boost::system::error_code());
}
}
void handleReadBody(ip::tcp::socket *socket,
boost::uint8_t type, boost::uint8_t *payLoad, boost::uint16_t len,
size_t readLen, const boost::system::error_code& error)
{
if(error)
{
delete[] payLoad;
handleReadError(error);
}
else
{
assert(len == readLen);
//passes the packet to the appropriate function for the type
//you could also use a switch statement or whatever
//to get the next packet you must call StartRead again
//personally I choose to do this from the actaul handler
//themselves
handlePacket(type,payLoad,len,error);
}
}
精彩评论