Для переноса структуры по сети необходимо следующее:
Упакуйте структуру.Для gcc и совместимых компиляторов, сделайте это с __attribute__((packed))
.
Не используйте никаких элементов, кроме целых чисел без знака фиксированного размера, других упакованных структур, удовлетворяющих этим требованиям, или массивов любых избывший.Целые числа со знаком тоже хороши, если только ваша машина не использует представление дополнения до двух.
Решите, будет ли ваш протокол использовать кодирование целых чисел с прямым или младшим порядком байтов.Делайте преобразования при чтении и записи этих целых чисел.
Кроме того, не принимают указатели членов упакованной структуры , за исключением тех, которые имеют размер 1 или другую вложенную упакованную структуруструктур.См. этот ответ .
Ниже приведен простой пример кодирования и декодирования.Предполагается, что функции преобразования порядка байтов hton8()
, ntoh8()
, hton32()
и ntoh32()
доступны (первые две не используются, но для согласованности).
#include <stdint.h>
#include <inttypes.h>
#include <stdlib.h>
#include <stdio.h>
// get byte order conversion functions
#include "byteorder.h"
struct packet {
uint8_t x;
uint32_t y;
} __attribute__((packed));
static void decode_packet (uint8_t *recv_data, size_t recv_len)
{
// check size
if (recv_len < sizeof(struct packet)) {
fprintf(stderr, "received too little!");
return;
}
// make pointer
struct packet *recv_packet = (struct packet *)recv_data;
// fix byte order
uint8_t x = ntoh8(recv_packet->x);
uint32_t y = ntoh32(recv_packet->y);
printf("Decoded: x=%"PRIu8" y=%"PRIu32"\n", x, y);
}
int main (int argc, char *argv[])
{
// build packet
struct packet p;
p.x = hton8(17);
p.y = hton32(2924);
// send packet over link....
// on the other end, get some data (recv_data, recv_len) to decode:
uint8_t *recv_data = (uint8_t *)&p;
size_t recv_len = sizeof(p);
// now decode
decode_packet(recv_data, recv_len);
return 0;
}
Что касается функций преобразования порядка байтов, то в вашей системе htons()
/ ntohs()
и htonl()
/ ntohl()
можно использовать для 16- и 32-разрядных целых чисел, соответственно, для преобразования в / из большихобратный порядок байт.Тем не менее, я не знаю какой-либо стандартной функции для 64-битных целых чисел или для преобразования в / из прямым порядком байтов.Вы можете использовать мои функции преобразования порядка байтов ;если вы сделаете это, вы должны указать порядок байтов вашей машины, определив BADVPN_LITTLE_ENDIAN
или BADVPN_BIG_ENDIAN
.
Что касается целых чисел со знаком, функции преобразования могут быть реализованыбезопасно так же, как те, которые я написал и связал (напрямую меняя байты);просто измените без знака на подписанный.
ОБНОВЛЕНИЕ : если вы хотите эффективный двоичный протокол, но не любите возиться с байтами, вы можете попробовать что-то вроде Protocol Buffers ( C реализация ).Это позволяет вам описывать формат ваших сообщений в отдельных файлах и генерирует исходный код, который вы используете для кодирования и декодирования сообщений указанного вами формата.Я тоже реализовал нечто подобное, но значительно упростил;см. мой генератор BProto и некоторые примеры (см. файлы .bproto и addr.h для примера использования).