Где реализовать протокол, используя boost :: asio? - PullRequest
8 голосов
/ 18 ноября 2011

Я пытаюсь реализовать простой протокол последовательного порта.Это выглядит так:

  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);, где myPacket - это Packet объект)

Когда 0xff видно в любом месте полученных данных, это означает, что начинается новый пакет.Поэтому, когда 0xff получено, он должен забыть все предыдущие полученные данные и начать получать новый пакет.

Я планирую использовать асинхронные операции и добавить таймауты.

Итак, мой вопрос: где реализовать такой протокол?Или, в более общем случае, где реализовать протокол, используя boost::asio.Я не ищу рабочий код, но что-то вроде совета о том, где реализовать протокол и какую функциональность boost::asio использовать.

update:

Нет управления потоком (аппаратное или программное обеспечение)используется в этом случае.

Ответы [ 2 ]

2 голосов
/ 18 ноября 2011

Прежде всего, как указал @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

Обратите внимание, что в этом примере предполагается, что оба пира имеют одинаковые порядковые номера.

Я написал этот код здесь, поэтому не ожидайте, что он правильный, используйте его просто как идею.

0 голосов
/ 18 ноября 2011

При реализации протоколов я ранее находил конечные автоматы очень полезными.

Хотя я не использовал 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 для реализации конечного автомата.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...