Десериализация байтового массива в структуру

c++ struct bytearray serialization

6187 просмотра

6 ответа

2126 Репутация автора

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

#pragma pack(1)

struct Header
{
    unsigned short bodyLength;
    int msgID;
    unsigned short someOtherValue;
    unsigned short protocolVersion;
};

int main()
{
    boost::array<char, 128> msgBuffer;
    Header header;

    for(int x = 0; x < sizeof(Header); x++)
        msgBuffer[x] = 0x01; // assign some values

    memcpy(&header, msgBuffer.data(), sizeof(Header));

    system("PAUSE");    

    return 0;
}

Будет ли это всегда работать при условии, что структура никогда не содержит полей переменной длины? Есть ли независимый от платформы / идиоматический способ сделать это?

Примечание:

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

Автор: drby Источник Размещён: 06.02.2009 11:15

Ответы (6)


5 плюса

15918 Репутация автора

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

И даже на распространенных x86-упакованных структурах код может выполняться медленнее.

Также вам придется позаботиться о работе с различными платформами с прямым порядком байтов.

Кстати, если вам нужен простой и независимый от платформы механизм связи с привязками ко многим языкам программирования, взгляните на YAMI .

Автор: Anonymous Размещён: 06.02.2009 11:30

5 плюса

335619 Репутация автора

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

Эта вторая ссылка специфична для GCC, но это относится ко всем компиляторам.

Я рекомендую читать поля побайтно и собирать большие поля (целые и т. Д.) Из этих байтов. Это дает вам контроль над порядком байтов и дополнением.

Автор: unwind Размещён: 06.02.2009 11:35

2 плюса

8821 Репутация автора

#pragma pack(1)Директива должна работать на большинстве компиляторов , но вы можете проверить, работая, как велика ваша структура данных должна быть (10 в вашем случае , если мои математика правильно) и использовать , printf("%d", sizeof(Header));чтобы проверить , что упаковка делается.

Как уже говорили другие, вы все равно должны опасаться Endianness, если вы идете между архитектурами.

Автор: Mark Pim Размещён: 06.02.2009 11:35

0 плюса

2126 Репутация автора

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

Так как насчет этого:

const int kHeaderSizeInBytes = 6;

struct Header
{
    unsigned short bodyLength;
    unsigned short msgID;
    unsigned short protocolVersion; 

    unsigned short convertUnsignedShort(char inputArray[sizeof(unsigned short)])
        {return (((unsigned char) (inputArray[0])) << 8) + (unsigned char)(inputArray[1]);}

    void operator<<(char inputArray[kHeaderSizeInBytes])
    {
        bodyLength = convertUnsignedShort(inputArray);
        msgID = convertUnsignedShort(inputArray + sizeof(bodyLength));
        protocolVersion = convertUnsignedShort(inputArray + sizeof(bodyLength) + sizeof(msgID));
    }
};

int main()
{
    boost::array<char, 128> msgBuffer;
    Header header;

    for(int x = 0; x < kHeaderSizeInBytes; x++)
        msgBuffer[x] = x;

    header << msgBuffer.data();

    system("PAUSE");    

    return 0;
}

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

Автор: drby Размещён: 06.02.2009 12:16

1 плюс

1881 Репутация автора

Я категорически не согласен с идеей чтения побайтов. Если вы позаботитесь об упаковке структуры в объявлении структуры, вы можете скопировать в структуру без проблем. Для проблемы endiannes повторное чтение побайтно решает проблему, но не дает общего решения. Этот метод очень хромает. Я делал что-то подобное раньше для аналогичной работы, и все работало без сбоев.

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

Например, определение структуры, которую вы дали выше, "sis s". (s = short, i = int) Затем я передаю адрес структуры, это определение и опцию упаковки структуры этой структуры специальной функции, которая имеет дело с endiannes и вуаля, как это делается.

SwitchEndianToBig (& header, "sis s", 4); // 4 = опция упаковки структуры

Автор: Malkocoglu Размещён: 06.02.2009 12:27

1 плюс

42678 Репутация автора

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

#include <array>
#include <algorithm>

//#pragma pack(1) // not needed

struct Header
{
    unsigned short bodyLength;
    int msgID;
    unsigned short someOtherValue;
    unsigned short protocolVersion;
    float testFloat;

    Header() : bodyLength(42), msgID(34), someOtherValue(66), protocolVersion(69), testFloat( 3.14f ) {}
};

int main()
{
    std::tr1::array<char, 128> msgBuffer;
    Header header;

    const char* rawData = reinterpret_cast< const char* >( &header );

    std::copy( rawData, rawData + sizeof(Header), msgBuffer.data()); // assuming msgBuffer is always big enough

    system("PAUSE");    

    return 0;
}

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

Автор: Klaim Размещён: 06.02.2009 06:07
Вопросы из категории :
32x32