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

时间:2011-11-18 06:53:18

标签: c++ protocols boost-asio

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

  1. 在收到0xff之前丢弃所有数据
  2. 读取标头(节点地址和数据长度,4个字节)
  3. 读取数据(最多64个字节)
  4. 读crc
  5. 处理收到的数据包
  6. 发送回复
  7. 当看到0xff时,即使数据中间没有预料到,也意味着收到了新的数据包
  8. 我可以使用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功能。

    更新

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

2 个答案:

答案 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以实现状态机。