Как интерпретировать двоичные данные в C ++? - PullRequest
5 голосов
/ 12 мая 2009

Я отправляю и получаю двоичные данные в / из устройства в пакетах (64 байта). Данные имеют определенный формат, части которого различаются в зависимости от запроса / ответа.

Сейчас я разрабатываю интерпретатор для полученных данных. Простое чтение данных по позициям - это нормально, но не выглядит так здорово, когда у меня дюжина разных форматов ответов. В настоящее время я думаю о создании нескольких структур для этой цели, но я не знаю, как это будет с заполнением.

Может быть, есть лучший способ?


Связанный:

Ответы [ 5 ]

8 голосов
/ 12 мая 2009

Вам нужно использовать структуры и / или союзы. Вам нужно будет убедиться, что ваши данные правильно упакованы на обеих сторонах соединения, и вы можете захотеть преобразовать их в сетевой порядок байтов и на каждом конце, если есть вероятность того, что любая из сторон соединения может работать с другим порядок байтов.

Как пример:

#pragma pack(push)  /* push current alignment to stack */
#pragma pack(1)     /* set alignment to 1 byte boundary */
typedef struct {
    unsigned int    packetID;  // identifies packet in one direction
    unsigned int    data_length;
    char            receipt_flag;  // indicates to ack packet or keep sending packet till acked
    char            data[]; // this is typically ascii string data w/ \n terminated fields but could also be binary
} tPacketBuffer ;
#pragma pack(pop)   /* restore original alignment from stack */

, а затем при назначении:

packetBuffer.packetID = htonl(123456);

, а затем при получении:

packetBuffer.packetID = ntohl(packetBuffer.packetID);

Вот некоторые обсуждения Endianness и Выравнивание и структура упаковки

Если вы не упакуете структуру, она в конечном итоге будет выровнена по границам слов, а внутренняя структура структуры и ее размер будут неправильными.

3 голосов
/ 13 мая 2009

Я делал это бесчисленное количество раз раньше: это очень распространенный сценарий. Есть ряд вещей, которые я практически всегда делаю.

Не беспокойтесь о том, чтобы сделать его наиболее эффективным из доступных.

Если мы тратим много времени на упаковку и распаковку пакетов, то мы всегда можем изменить это, чтобы сделать его более эффективным. Хотя я еще не сталкивался со случаем, когда мне пришлось это делать, я не реализовывал сетевые маршрутизаторы!

Хотя использование структур / объединений является наиболее эффективным подходом с точки зрения времени выполнения, оно сопряжено с рядом сложностей: убедить компилятор упаковать структуры / объединения, чтобы они соответствовали структуре октетов нужных вам пакетов, стараясь избежать выравнивания проблемы с порядком байтов и отсутствием безопасности, так как нет возможности или почти нет возможности проверять работоспособность отладочных сборок.

Я часто сталкиваюсь с архитектурой, включающей в себя следующие вещи:

  • Базовый класс пакета. Любые общие поля данных доступны (но не могут быть изменены). Если данные не хранятся в упакованном формате, то есть виртуальная функция, которая создаст упакованный пакет.
  • Количество классов представления для определенных типов пакетов, полученных из общего типа пакета. Если мы используем функцию упаковки, то каждый класс представления должен реализовать ее.
  • Все, что может быть выведено из определенного типа класса представления (то есть идентификатора типа пакета из общего поля данных), рассматривается как часть инициализации и не может быть изменено в противном случае.
  • Каждый класс представления может быть создан из неупакованного пакета или будет терпеть неудачу, если данные пакета недопустимы для этого типа. Это может быть упаковано на фабрике для удобства.
  • Если у нас нет доступных RTTI, мы можем получить «RTTI для бедняков», используя идентификатор пакета, чтобы определить, к какому конкретному классу представления относится объект.

Во всем этом можно (даже если только для отладочных сборок) проверить, что для каждого изменяемого поля установлено правильное значение. Хотя это может показаться большой работой, очень сложно получить неверно отформатированный пакет, содержимое предварительно упакованных пакетов может быть легко проверено на глаз с помощью отладчика (поскольку все это в обычных переменных формата, встроенных в платформу).

Если нам нужно реализовать более эффективную схему хранения, это тоже может быть включено в эту абстракцию с небольшими дополнительными затратами на производительность.

3 голосов
/ 12 мая 2009

Трудно сказать, что является лучшим решением, не зная точного формата (ов) данных. Вы рассматривали возможность использования союзов?

1 голос
/ 15 мая 2009

Это «готовое» решение, но я бы посоветовал взглянуть на библиотеку Python construct .

Construct является библиотекой Python для парсинг и сбор данных структуры (двоичные или текстовые). это основанный на концепции определения данных структуры декларативным образом, а не процедурный код: больше сложные конструкции состоят из иерархия более простых. Это первая библиотека, которая делает разбор веселым, вместо обычной головной боли это сегодня.

конструкция очень надежная и мощная, и только чтение учебника поможет вам лучше понять проблему. У автора также есть планы по автоматической генерации кода на C из определений, поэтому об этом стоит прочитать.

1 голос
/ 13 мая 2009

Я согласен с Wuggy. Вы также можете использовать генерацию кода для этого. Используйте простой файл определения данных, чтобы определить все типы пакетов, а затем запустите на нем скрипт python, чтобы сгенерировать структуры прототипов и функции сериализации / десериализации для каждого из них.

...