Отправить структуру через сокет с правильным заполнением и порядком байтов в C - PullRequest
5 голосов
/ 14 апреля 2011

У меня есть несколько структур, определенных для отправки через разные операционные системы (сети TCP).Определенные структуры:

struct Struct1 { uint32_t num; char str[10]; char str2[10];}
struct Struct2 { uint16_t num; char str[10];}   

typedef Struct1 a;
typedef Struct2 b;

Данные хранятся в текстовом файле.Формат данных таков:

  • 123
  • Пирог
  • Корка

Struct1 a сохраняется как 3 отдельных параметра.Однако struct2 - это два отдельных параметра, в которых 2-я и 3-я строки хранятся в char str [].Проблема в том, что когда я пишу на сервер по нескольким сетям, данные не принимаются правильно.Есть множество пространств, которые разделяют различные параметры в структурах.Как обеспечить правильную отправку и заполнение при записи на сервер?Как правильно хранить данные (динамический буфер или фиксированный буфер)?

Пример записи: write (fd, & a, sizeof (typedef struct a));Правильно ли это?

Проблема Вывод на стороне приема для struct2:

  • 123 (,)
  • 0 (, Pie)
  • 0 (Crust,)

Правильный вывод

123 (пирог, корочка)

Ответы [ 4 ]

10 голосов
/ 14 апреля 2011

write(fd,&a, sizeof(a)); неверно;по крайней мере, не переносимо, так как компилятор C может вводить отступ между элементами для обеспечения правильного выравнивания.sizeof(typedef struct a) даже не имеет смысла.

Способ отправки данных зависит от спецификаций вашего протокола.В частности, протоколы определяют широко различные способы отправки строк.Как правило, наиболее безопасно отправлять struct участников отдельно;или несколькими звонками, чтобы написать или writev(2).Например, чтобы отправить

struct { uint32_t a; uint16_t b; } foo;

по сети, где foo.a и foo.b уже имеют правильный порядковый номер, вы должны сделать что-то вроде:

struct iovec v[2];
v[0].iov_base = &foo.a;
v[0].iov_len  = sizeof(uint32_t);
v[1].iov_base = &foo.b;
v[1].iov_len  = sizeof(uint16_t);
writev(fp, v, 2);
3 голосов
/ 14 апреля 2011

Отправка структур по сети - это сложно. Следующие проблемы могут возникнуть

  1. Байт endiannes выдает целые числа.
  2. Заполнение введено вашим компилятором.
  3. Разбор строк (т.е. определение границ строк).

Если производительность не является вашей целью, я бы предложил создать кодеры и декодеры для каждой структуры для отправки и получения (ASN.1, XML или пользовательская). Если производительность действительно требуется, вы все равно можете использовать структуры и решить (1), исправив порядковый номер (то есть сетевой байт порядок) и убедитесь, что ваши целые числа хранятся как таковые в этих структурах, и (2) исправив компилятор и используя прагмы или атрибуты для реализации «упакованной» структуры.

Gcc, например, использует атрибут (( упакованный )) как таковой:

struct mystruct {
  uint32_t  a;
  uint16_t b;
  unsigned char text[24];
} __attribute__((__packed__));

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

1 голос
/ 14 апреля 2011

Существуют функции преобразования для обеспечения переносимости двоичных целых чисел по сети.Используйте htons, htonl, ntohs и ntohl для преобразования 16- и 32-разрядных целых чисел из хоста в сетевой порядок байтов и наоборот.

1 голос
/ 14 апреля 2011

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

...