Десериализация структурных данных, отправленных системой Big Endian в систему Little Endian - PullRequest
0 голосов
/ 10 февраля 2011

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

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

struct CCClntPkt
{
    unsigned short packet_type;
    unsigned short reply_socket;
    unsigned long  msg_ID;
    unsigned short msg_length;
    unsigned char  client_string[250];
};

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

char BCpacket_in[MAX_PACKET];
struct CCClntPkt *pClntPkt;

<snip>

rcv_cnt = recvfrom(BCServerSocket, BCpacket_in,
                sizeof(BCpacket_in),0x0,(struct sockaddr *)&from,
                &fromlen);

if (rcv_cnt > 0)
{
    pClntPkt = (struct CCClntPkt *) &BCpacket_in;
}

Мне удалось получить правильные значения для packet_type и reply_socket с помощью ntohs, но символьное поле client_string искажено. Я также попытался поместить pragma pack(1) до и pragma pack(0) после структуры, но, похоже, проблема с выравниванием по-прежнему существует.

Я также пробовал значения сдвига битов из BCpacket_in и смог получить правильные значения для packet_type и reply_socket, но не могу понять, как извлечь ulong msg_ID. Код для этого был:

packet_type = BCpacket_in[0] << 8;
packet_type |= BCpacket_in[1];

reply_to_socket = BCpacket_in[2] << 8;
reply_to_socket |= BCpacket_in[3];

/*
msg_ID = BCpacket_in[4] << 24;
msg_ID |= BCpacket_in[5] << 16;
msg_ID |= BCpacket_in[6] << 8;
msg_ID |= BCpacket_in[7];
*/

На данный момент я довольно озадачен, поэтому любая помощь приветствуется. Я не являюсь первоначальным автором этой программы, и мои знания C довольно ограничены. Я не возражаю против выполнения этой работы, поэтому я был бы признателен, если бы мне предоставили соответствующие ссылки. Спасибо!

Ответы [ 4 ]

3 голосов
/ 10 февраля 2011

Вам нужно будет вручную разобрать полученный пакет (BCpacket_in) в пакет struct CCClntPkt, это единственный переносимый способ сделать это. Переводы Endianness правильно обрабатываются с помощью семейства функций ntohl (сеть-хост long); см. страницы byteorder(3) и endian(3).

В этих функциях предполагается, что все пакеты передаются по проводам в формате big-endian, потому что это интернет-стандарт.

2 голосов
/ 11 февраля 2011

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

Если вы скомпилируете эту программу на обоих ваших хостах, она покажет вам размеры имакет struct:

#include <stddef.h>
#include <stdio.h>

struct CCClntPkt
{
    unsigned short packet_type;
    unsigned short reply_socket;
    unsigned long  msg_ID;
    unsigned short msg_length;
    unsigned char  client_string[250];
};

int main()
{
    printf("sizeof(unsigned short) = %u\n", (unsigned)sizeof(unsigned short));
    printf("sizeof(unsigned long) = %u\n", (unsigned)sizeof(unsigned long));

    printf("offsetof(struct CCClntPkt, reply_socket) = %u\n", (unsigned)offsetof(struct CCClntPkt, reply_socket));
    printf("offsetof(struct CCClntPkt, msg_ID) = %u\n", (unsigned)offsetof(struct CCClntPkt, msg_ID));
    printf("offsetof(struct CCClntPkt, msg_length) = %u\n", (unsigned)offsetof(struct CCClntPkt, msg_length));
    printf("offsetof(struct CCClntPkt, client_string) = %u\n", (unsigned)offsetof(struct CCClntPkt, client_string));

    return 0;
}

В частности, вполне возможно, что long длиннее на вашем новом хосте, чем на старом.Это может быть хорошим местом для использования типов точной ширины C99 от <stdint.h> - если на исходном хосте short - это 16-битный тип, а long - 32-битный, замените их на uint16_t и uint32_t соответственно.

Затем можно использовать ntohs() и ntohl() для выполнения коррекции порядка байтов.

1 голос
/ 10 февраля 2011
msg_ID = BCpacket_in[4] << 24;
msg_ID |= BCpacket_in[5] << 16;
msg_ID |= BCpacket_in[6] << 8;
msg_ID |= BCpacket_in[7];

Мне кажется, это правильно.

Попробуйте использовать unsigned char для своего буфера, чтобы защитить себя от проблемы подписи.

Кстати, msg_id с прямым порядком байтов, и вы уверены в смещении: как вы сказали, «упаковка» не работает на стороне клиента, поэтому можно сделать вывод, что структура отправляется в провод с использованием правил упаковки мэйнфрейм.

0 голосов
/ 10 февраля 2011

Это то, что я обычно делаю для упакованных структур, отправленных / полученных в / из сети:

#define PACKED __attribute__((__packed__))
struct PACKED message { ... };

Это зависит от GCC, см. здесь Затем вы должны выяснить, какого размера long здесь.Это отличается на 32- и 64-битных платформах.Возможно, вы захотите изучить использование stdint.h типов.Также смотрите информацию __builtin_bswap32() и __builtin_bswap64() GCC intrinsics .

...