Как интерпретировать двоичные данные в C++?

StackOverflow https://stackoverflow.com/questions/852334

  •  21-08-2019
  •  | 
  •  

Вопрос

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

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

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


Связанный:

Это было полезно?

Решение

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

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

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

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

Я часто получаю архитектуру, включающую следующие вещи:

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

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

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

Другие советы

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

В качестве примера:

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

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

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

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

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top