提升 ASIO 在基于 TCP 的协议中查找消息的开头

boost asio find beginning of message in tcp based protocol

本文关键字:协议 消息 开头 查找 TCP ASIO 提升      更新时间:2023-10-16

我想为通过 tcp 发送数据并使用以下协议的传感器实现一个客户端:

  1. 消息头以类型uint32的字节序列0xAFFEC0CC2开头
  2. 标头总共为 24 字节长(包括开始序列(,并包含消息正文的大小(以字节为单位(作为uint32
  3. 消息正文直接在标头之后发送,而不是由半音终止

目前,我得到了以下代码(假设存在连接的套接字(

typedef unsigned char byte;
boost::system::error_code error;
boost::asio::streambuf buf;
std::string magic_word_s = {static_cast<char>(0xAF), static_cast<char>(0xFE),
static_cast<char>(0xC0), static_cast<char>(0xC2)};
ssize_t n = boost::asio::read_until(socket_, buf, magic_word_s, error);
if(error)
std::cerr << boost::system::system_error(error).what() << std::endl;
buf.consume(n);
n = boost::asio::read(socket_, buf, boost::asio::transfer_exactly(20);
const byte * p = boost::asio::buffer_cast<const byte>(buf.data());
uint32_t size_of_body = *((byte*)p);

不幸的是,read_until文档备注:

成功执行read_until操作后,streambuf 可能包含分隔符以外的其他数据。应用程序通常会将该数据保留在 streambuf 中,以供后续read_until操作进行检查。

这意味着我失去了与所描述协议的同步。 有没有一种优雅的方法可以解决这个问题?

嗯...正如它所说...您只需将其"保留"在对象中,或将其临时存储在另一个对象中,并在完成时处理整个消息(下面称为"数据包"(。

在我的一个项目中,我也有类似的方法。我将解释一下我是如何做到的,这应该可以让您大致了解如何正确处理数据包。

在我的读取处理程序(回调(中,我一直在检查数据包是否完整。元数据信息(您的标头(临时存储在与远程合作伙伴关联的地图中(map<RemoteAddress,InfoStructure>(。

例如,它可能看起来像这样:

4 byte identifier
4 byte message-length
n byte message

处理传入数据,检查标识符+消息长度是否已经接收,继续检查消息数据是否用接收到的数据完成。
将数据包的其余部分留在临时缓冲区中,擦除旧数据。
当下一个数据包到达时继续处理,或检查接收的数据是否已经完成下一个数据包...

这种方法可能听起来有点慢,但我甚至可以在慢速机器上使用 SSL 10MB/s+。
如果没有SSL,更高的传输速率是可能的。

使用此方法,您还可以查看read_some或其异步版本。