我正在从串行设备中读取每个消息必须特别请求的地方。例如。您发送请求并获得序列化有效负载的响应。如何在连续读取async_read_until后使用asio缓冲区
每个消息包含在顺序包括以下部分:
- 前导码(2个字节, “$ M”)
- HEADER(3个字节,包含有效载荷长度N)
- PAYLOAD + CRC(N + 1个字节)
我与ASIO的方法是通过使用asio::async_read_until
事后使用asio::async_read
用于读取的字节的确切的量对于HEADER以检测消息的开始(前同步码),并PAYLOAD + CRC。由于消息末尾没有静态模式,因此我无法使用async_read_until
来阅读完整消息。
收到PREAMBLE后,async_read_until
的处理程序被调用,缓冲区包含PREAMBLE字节,并可能包含来自HEADER和PAYLOAD + CRC的附加字节。 为async_read_until的ASIO文件说:
成功async_read_until操作后,流缓冲可以 含有超出分隔符附加数据。应用程序 通常将该数据保留在streambuf中以供随后的async_read_until操作检查。
我解释这是因为你应该只消耗请求的字节,并将所有剩余的字节留在缓冲区中作进一步读取。 但是,由于数据已经存在于缓冲区中并且设备上没有任何内容,因此所有连续的读取块都将被阻止。
该读取是作为小型状态机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();
}
序言处理器onPreamble
接收到前导时,被称为:
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
中的剩余字节,因为不能保证这些字节将包含完整的消息。