在哪里使用 boost::asio 实现协议?

发布于 2024-12-16 13:29:51 字数 1321 浏览 3 评论 0原文

我正在尝试实现一个简单的串行端口协议。它是这样的:

  1. 丢弃所有数据,直到收到 0xff
  2. 读取标头(节点地址和数据长度,4 字节)
  3. 读取数据(最多 64 字节)
  4. 读取 crc
  5. 处理接收到的数据包
  6. 发送响应
  7. 时 >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); 其中 myPacketPacket 对象)

0xff 出现在接收到的数据,这意味着一个新的数据包正在开始。因此,当接收到0xff时,它必须忘记之前接收到的任何数据并开始接收新的数据包。

我计划使用异步操作并添加超时。

所以,我的问题是:在哪里实施这样的协议?或者更一般地说,在哪里使用 boost::asio 实现协议。我不是在寻找工作代码,而是在寻找关于在哪里实现协议以及使用哪些 boost::asio 功能的建议。

更新:

这种情况下没有使用流量控制(硬件或软件)。

I am trying to implement a simple serial port protocol. It goes like this:

  1. discard all data until 0xff is received
  2. read header (node address and data length, 4 bytes)
  3. read data (max. 64 bytes)
  4. read crc
  5. process the received packet
  6. send response
  7. when 0xff is seen, even when not expected like in the middle of the data, it means a new packet is received

I can implement this using boost::asio::serial_port with boost::asio::read() reading a single byte and processing this byte when it is received. Although this works, I was wondering if there is a more 'boost' like way to do this?

I looked at boost::asio::read_until() for reading until 0xff, but then I don't know how to discard the data. Storing the data in a buffer and then not using the buffer seems a bit wasteful.

I can use boost::asio::read_until() for reading until the end of the packet but then the MatchCondition needs to have access to (the header of the packet in) the buffer. It seems the MatchCondition only gets an iterator to the first and last byte recently received.

Also, the data received using boost::asio::read() ends up in a stream_buf and I have to parse the received data into a Packet object. I can do this parsing inside Packet, in a separate ParsePacket object or somehow integrate it with boost::asio (something like boost::asio::read(serial, myPacket); where myPacket is a Packet object)

When 0xff is seen anywhere in the received data, it means a new packet is starting. So when 0xff is received, it must forget any previous received data and start receiving a new packet.

I am planning on using asynchronous operations and adding timeouts.

So, my question is: where to implement a protocol like this? Or more generally, where to implement a protocol using boost::asio. I am not looking for working code but something like advise on where to implement the protocol and which boost::asio functionality to use.

update:

There is no flow control (hardware or software) used in this case.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

画中仙 2024-12-23 13:29:51

首先,正如 @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

请注意,此示例假设两个对等方具有相同的字节序。

我在这里编写了这段代码,所以不要指望它是正确的,仅将其用作一个想法。

First of all, as @Autopulated pointed in comments, I want to warn you about using delimiters (your 0xFF) in binary protocols. It's dangerous technique (brings a lot of ambiguity) and requires complicated implementations. Even if you can guarantee that your data doesn't contain 0xFF bytes you cannot do this about CRC field.

I would recommend to not bother with any delimiters and concentrate on simple and predictable binary protocol: [packet][packet]... where [packet] = [node address:X][data length:4][data:data length][CRC:1]

Sending such packets can look like:

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);

Receiving:

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

Please note that this example makes assumption that both peers has the same endianess.

I wrote this code here so don't expect it's correct, use it just as an idea.

﹉夏雨初晴づ 2024-12-23 13:29:51

在实现协议时,我之前发现状态机非常有用。

虽然我没有使用过 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 用于实现状态机。

When implementing protocols, I have previously found state-machines very useful.

Whilst I haven't used asio, this technique may be applicable here.

These can be implemented in C++ as an enum, a loop and a switch as follows:

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:
            ...
    }
}

You would need to add more flags to keep track of your progress within the protocol.

You may also want to look at boost::statechart for implementing the state machine.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文