gpt4 book ai didi

c++ - 如何在 async_read_until 之后使用 asio 缓冲区进行连续读取

转载 作者:行者123 更新时间:2023-11-30 05:12:31 24 4
gpt4 key购买 nike

我正在从串行设备读取,其中必须特别请求每条消息。例如。您发送请求并获得带有序列化负载的响应。

每条消息按顺序包含这些部分:

  1. 序言(2 字节,“$M”)
  2. HEADER(3字节,包含有效载荷长度N)
  3. 有效载荷+CRC(N+1字节)

我使用 asio 的方法是使用 asio::async_read_until 检测消息的开始(PREAMBLE),然后使用 asio::async_read 读取确切的数量HEADER 和 PAYLOAD+CRC 的字节数。由于消息末尾没有静态模式,我无法使用 async_read_until 读取完整消息。

收到 PREAMBLE 后,async_read_until 的处理程序被调用,缓冲区包含 PREAMBLE 字节,并且可能包含来自 HEADER 和 PAYLOAD+CRC 的额外字节。async_read_until 的 asio 文档说:

After a successful async_read_until operation, the streambuf may contain additional data beyond the delimiter. An application will typically leave that data in the streambuf for a subsequent async_read_until operation to examine.

我将其解释为您应该只消耗请求的字节并将所有剩余字节留在缓冲区中以供进一步读取。 但是,所有连续读取都会阻塞,因为数据已经在缓冲区中并且设备上没有任何剩余。

读取被实现为一个小型状态机processState,其中根据要读取消息的哪一部分注册不同的处理程序。所有读取都是使用相同的 buffer (asio::streambuf) 完成的。 processState 在无限循环中被调用。

void processState() {
// register handler for incomming messages
std::cout << "state: " << parser_state << std::endl;
switch (parser_state) {
case READ_PREAMBLE:
asio::async_read_until(port, buffer, "$M",
std::bind(&Client::onPreamble, this, std::placeholders::_1, std::placeholders::_2));
break;
case READ_HEADER:
asio::async_read(port, buffer, asio::transfer_exactly(3),
std::bind(&Client::onHeader, this, std::placeholders::_1, std::placeholders::_2));
break;
case READ_PAYLOAD_CRC:
asio::async_read(port, buffer, asio::transfer_exactly(request_received->length+1),
std::bind(&Client::onDataCRC, this, std::placeholders::_1, std::placeholders::_2));
break;
case PROCESS_PAYLOAD:
onProcessMessage();
break;
case END:
parser_state = READ_PREAMBLE;
break;
}
// wait for incoming data
io.run();
io.reset();
}

PREAMBLE 处理程序 onPreamble 在接收到 PREAMBLE 时被调用:

void onPreamble(const asio::error_code& error, const std::size_t bytes_transferred) {
std::cout << "onPreamble START" << std::endl;
if(error) { return; }

std::cout << "buffer: " << buffer.in_avail() << "/" << buffer.size() << std::endl;

// ignore and remove header bytes
buffer.consume(bytes_transferred);

std::cout << "buffer: " << buffer.in_avail() << "/" << buffer.size() << std::endl;

buffer.commit(buffer.size());

std::cout << "onPreamble END" << std::endl;
parser_state = READ_HEADER;
}

在这个处理程序之后,没有其他处理程序被调用,因为数据在缓冲区中并且没有数据留在设备上。

使用 asio::streambuf 的正确方法是什么,以便调用连续 async_read 的处理程序,我可以按状态顺序处理字节机器? 我不想处理 onPreamble 中的剩余字节,因为不能保证这些字节将包含完整消息。

最佳答案

您不需要在 onPreamble() 处理程序中调用 buffer.commit()。调用 buffer.consume() 将按照您的预期删除 header 字节,并将剩余字节(如果收到)留在 asio::streambuf 中以供下一次读取。 streambuf 的 prepare()commit() 调用在您用数据填充以发送给远程方时使用。

我刚刚完成了一个 blog post and codecast关于使用 asio::streambuf 对一些 Web 服务器执行简单的 HTTP GET。它可能会让您更好地了解如何使用 async_read_until()async_read()

关于c++ - 如何在 async_read_until 之后使用 asio 缓冲区进行连续读取,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44489122/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com