Тут уже писал про проблему пересылки/приёмки структурированных данных по сети.
На нашей целевой платформе нельзя применять сторонние библиотеки (типа libpack, как в предыдущей статье), и, так вышло (лучи ненависти), что #pragma pack() то работает, то не работает, да ещё стоит условие, что данные должны быть преобразованы к сетевому виду (ntohs(), ntohl(), htons(), htonl()), поэтому я сделал на макросах такой фит ушами:
/**************************************************************************************************/
/* Паковка и распаковка данных, для передачи по сети ---------------------------------------------*/
/**************************************************************************************************/
// Распаковка массива в структуры
/**
Начало блока распаковки массива данных, на которые указывает <br/>c ptr и размера <br/>c size
*/
#define unpack_begin(ptr, size) { <br/>
size_t __addr = 0; <br/>
size_t __size = (size_t)(size); <br/>
uint8_t *__ptr = (uint8_t*)(ptr); <br/>
union { <br/>
uint8_t cc[8]; <br/>
uint16_t ii[4]; <br/>
uint32_t ll[2]; <br/>
float ff[2]; <br/>
double dd; <br/>
} __swapd; (void)(__swapd)
/**
Распаковка очередной порции данных в <br/>c target
*/
#define unpack_next(target) <br/>
pack_printf("Unpack <br/>"%s<br/>"... Size: %d, current offset: %d, current size: %d<br/>n", #target, __size, __addr, sizeof(target)); <br/>
if ((__addr + (int)sizeof(target)) <= (__size)) <br/>
{ <br/>
memcpy(&(target), __ptr + __addr, sizeof(target)); <br/>
__addr += sizeof(target); <br/>
} (void)(0)
/**
Распаковка очередной порции данных в <br/>c target, с преобразованием из сетевого вида
Сетевое преобразование работает для простых типов размерос 2 (short), 4 (int, float)
и 8 (lint32_tlint32_t double) байтint16_t остальных данных
*/
#define unpack_net_next(target) <br/>
unpack_next(target); <br/>
switch(sizeof(target)) <br/>
{ <br/>
case 2: <br/>
memcpy(&__swapd.ii[0], &target, 2); <br/>
__swapd.ii[0] = ntohs(__swapd.ii[0]); <br/>
memcpy(&target, &__swapd.ii[0], 2); <br/>
break; <br/>
case 4: <br/>
memcpy(&__swapd.ll[0], &target, 4); <br/>
__swapd.ll[0] = ntohl(__swapd.ll[0]); <br/>
memcpy(&target, &__swapd.ll[0], 4); <br/>
break; <br/>
case 8: <br/>
memcpy(&__swapd.dd, &target, 8); <br/>
__swapd.ll[0] = ntohl(__swapd.ll[0]); <br/>
__swapd.ll[1] = ntohl(__swapd.ll[1]); <br/>
memcpy(&target, &__swapd.dd, 8); <br/>
break; <br/>
} (void)(0)
/**
Конец блока распаковки
*/
#define unpack_end() } (void)(0)
// Паковка данных из элементов струтуры в массив данных
/**
Начало блока паковки данных в <br/>c ptr размером <br/>c size
*/
#define pack_begin(ptr, size) unpack_begin(ptr, size)
// Внутренний вспомогательный макрос, как общая часть для pack_next()/pack_net_next()
#define _pack_next_intr(source, data) <br/>
printf("Pack <br/>"%s<br/>"... Size: %d, current offset: %d, current size: %d<br/>n", #source, __size, __addr, sizeof(source)); <br/>
if ((__addr + (int)sizeof(source)) <= (__size)) <br/>
{ <br/>
memcpy(__ptr + __addr, &(data), sizeof(source)); <br/>
__addr += sizeof(source); <br/>
} (void)(0)
/**
Пакует очередной элемент
*/
#define pack_next(source) <br/>
_pack_next_intr(source, source)
/**
Пакует очередной элемент, преобразовывая его в сетевой вид
*/
#define pack_net_next(source) <br/>
switch(sizeof(source)) <br/>
{ <br/>
case 2: <br/>
memcpy(&__swapd.ii[0], &source, 2); <br/>
__swapd.ii[0] = htons(__swapd.ii[0]); <br/>
_pack_next_intr(source, __swapd.ii[0]); <br/>
break; <br/>
case 4: <br/>
memcpy(&__swapd.ll[0], &source, 4); <br/>
__swapd.ll[0] = htonl(__swapd.ll[0]); <br/>
_pack_next_intr(source, __swapd.ll[0]); <br/>
break; <br/>
case 8: <br/>
memcpy(&__swapd.dd, &source, 8); <br/>
__swapd.ll[0] = htonl(__swapd.ll[0]); <br/>
__swapd.ll[1] = htonl(__swapd.ll[1]); <br/>
_pack_next_intr(source, __swapd.dd); <br/>
break; <br/>
default: <br/>
pack_next(source); <br/>
break; <br/>
} (void)(0)
/**
Конец блока паковки данных
*/
#define pack_end() unpack_end()
Можно ещё оптимизировать, тут оверхед с переменными вспомогательными, можно без них в большинстве случаев. Ну и, кажется, не совсем корректная работа с величинами больше 4 байт (double), но на серверной машине, по-моему, вообще залепа сделана, посмотрим как работать будет :)
Пример использования для отсылки данных:
struct Data {
short id;
float d;
long tm;
} data;
data.id = 0;
data.d = 73.3;
data.tm = 0xFEFD;
int n;
size_t data_size = 2 + 4 + 4; // sizeof(short) + sizeof(float) + sizeof(long) - поля структуры
char *data_ptr;
data_ptr = calloc(data_size, sizeof(char));
pack_begin(data_ptr, data_size);
pack_net_next(data.id);
pack_net_next(data.d);
pack_net_next(data.tm);
pack_end();
n = send(sock, data_ptr, data_size, 0);
Пример получения данных:
struct Data {
short id;
float d;
long tm;
} data;
int n;
size_t data_size = 2 + 4 + 4; // sizeof(short) + sizeof(float) + sizeof(long) - сколько данных получить нужно
char *data_ptr;
data_ptr = calloc(data_size, sizeof(char));
n = recv(sock, data_ptr, data_size, 0);
unpack_begin(data_ptr, data_size);
unpack_net_next(data.id);
unpack_net_next(data.d);
unpack_net_next(data.tm);
unpack_end();
// Тут структура заполнена распакованными данными
Если преобразование в сетевой вид не нужно, используйте pack/unpack_next()