我正在尝试实现一个简单的串行端口协议。它是这样的:
0xff
之前丢弃所有数据0xff
时,即使数据中间没有预料到,也意味着收到了新的数据包我可以使用boost::asio::serial_port
实现此操作,boost::asio::read()
读取单个字节并在接收到该字节时处理该字节。虽然这有效,但我想知道是否有更多的“提升”方式来做到这一点?
我查看boost::asio::read_until()
阅读直到0xff
,但后来我不知道如何丢弃这些数据。将数据存储在缓冲区中然后不使用缓冲区似乎有点浪费。
我可以使用boost::asio::read_until()
进行读取,直到数据包结束,但MatchCondition
需要访问缓冲区中的(数据包的标头)。似乎MatchCondition
只获得了最近收到的第一个和最后一个字节的迭代器。
此外,使用boost::asio::read()
收到的数据最终在stream_buf
中,我必须将收到的数据解析为Packet
个对象。我可以在Packet
内部,在单独的ParsePacket
对象中进行解析,或者以某种方式将其与boost::asio
集成(类似boost::asio::read(serial, myPacket);
,其中myPacket
是{{1}对象)
当在接收到的数据中的任何地方看到Packet
时,表示新数据包正在启动。因此,当收到0xff
时,它必须忘记以前收到的所有数据并开始接收新数据包。
我打算使用异步操作并添加超时。
所以,我的问题是:在哪里实现这样的协议?或者更一般地,在哪里使用0xff
实现协议。我不是在寻找工作代码,而是建议在何处实现协议以及使用哪种boost::asio
功能。
更新
在这种情况下,没有使用流量控制(硬件或软件)。
答案 0 :(得分:2)
首先,正如@Autopulated在评论中指出的那样,我想警告你在二进制协议中使用分隔符(你的0xFF)。这是危险的技术(带来很多歧义),需要复杂的实现。即使您可以保证您的数据不包含0xFF字节,也无法对CRC字段执行此操作。
我建议不要打扰任何分隔符并专注于简单且可预测的二进制协议:[packet][packet]...
其中[packet] = [node address:X][data length:4][data:data length][CRC:1]
发送此类数据包可能如下所示:
size_t const data_length_bytes = 4;
std::vector<char> data = ...;
size_t data_length = data.size();
Node_address node_address = ...;
std::vector<boost::asio::const_buffer> bufs;
bufs.push_back(boost::asio::buffer(&node_address, sizeof(node_address)));
bufs.push_back(boost::asio::buffer(&data_length, data_length_bytes));
bufs.push_back(boost::asio::buffer(data));
boost::system::error_code error;
boost::asio::write(socket, boost::asio::buffer(bufs), error);
if (error)
throw boost::system::system_error(error);
接收:
size_t data_length;
std::vector<char> data;
Node_address node_address;
char crc;
std::vector<boost::asio::mutable_buffer> bufs;
boost::system::error_code error;
bufs.push_back(boost::asio::buffer(&node_address, sizeof(node_address)));
bufs.push_back(boost::asio::buffer(&data_length, data_length_bytes));
boost::asio::read(serial_port, bufs, error);
if (error)
throw boost::system::system_error(error);
data.resize(data_length);
bufs.clear();
bufs.push_back(boost::asio::buffer(&data.front(), data_length));
bufs.push_back(boost::asio::buffer(&crc, sizeof(crc));
boost::asio::read(serial_port, bufs, error);
if (error)
throw boost::system::system_error(error);
// check CRC
// send response
请注意,此示例假设两个对等方都具有相同的endianess。
我在这里编写了这段代码,所以不要指望它是正确的,只是作为一个想法使用它。
答案 1 :(得分:0)
在实现协议时,我之前发现状态机非常有用。
虽然我没有使用asio
,但此技术可能适用于此。
这些可以在C ++中以enum
,循环和switch
实现,如下所示:
enum States { StateInitial, StateHeader, StateData ... };
States state = StateInitial;
while (1)
{
char ch = get_byte_function();
switch (state)
{
case StateInitial:
if (ch == '\xFF')
state = StateHeader;
break;
case StateHeader:
...
}
}
您需要添加更多标记以跟踪协议中的进度。
您可能还需要查看boost::statechart
以实现状态机。