Сериализация строк, целочисленных значений и чисел с плавающей точкой для символьных массивов для работы в сети без библиотек - PullRequest
9 голосов
/ 09 ноября 2009

Я хочу передавать данные по сети, но я не хочу использовать какие-либо сторонние библиотеки (Стандарт C / C ++ в порядке).

например:

unsigned int x = 123;
char y[3] = {'h', 'i', '\0'};
float z = 1.23f;

Я хочу это в

char xyz[11];

массив.

Примечание: Чтобы передать его по сети, мне нужен сетевой порядок байтов для unsigned int (функция htonl), затем мне нужно каким-то образом сериализовать число с плавающей запятой, чтобы оно было в форме IEEE 754 (есть много функций в Интернете), и я знаю это. *

Как я могу получить их в массив xyz-Array, аккуратно выстроенный в ряд, чтобы я мог использовать это как буфер для моей функции socket + send ()? Очевидно, у меня есть обратные функции (ntohl, и обратный IEEE 754), чтобы получить их, но там мне тоже нужна техника, желательно такая же ...

Было бы что-то вроде этого:

xyz in binary: 
00000000 0000000 00000000 01111011 | 01101000 | 01101001 | 00000000 | 00111111 10011101 01110000 10100100
- big endian repr. of u. int 123 - | - 'h'  - | - 'i'  - | - '\0' - | -   IEEE 754 repr of float 1.23   -

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

Ответы [ 4 ]

18 голосов
/ 09 ноября 2009

Ах, вы хотите сериализовать примитивные типы данных! В принципе, существует два подхода: первый заключается в том, что вы просто извлекаете внутреннее двоичное представление данных в памяти, которое вы хотите сериализовать, переосмысливаете его как символ и используете его в качестве своего представления:

Так что если у вас есть:

двойной д;

вы берете адрес этого, переосмысливаете этот указатель как указатель на символ, а затем используете эти символы:

double *pd=&d;
char *pc = reinterpret_cast<char*>(pd); 
for(size_t i=0; i<sizeof(double); i++) 
{
   char ch = *pc;   
   DoSomethingWith(ch);   
   pc++;
}

Это работает со всеми примитивными типами данных. Основная проблема заключается в том, что представление binray зависит от реализации (в основном от процессора). (И при попытке сделать это с IEEE NANs вы столкнетесь с небольшими ошибками ...).

В целом, этот подход вообще не переносим, ​​так как у вас нет никакого контроля над представлением ваших данных.

Второй подход заключается в использовании представления более высокого уровня, которое вы сами контролируете. Если производительность не является проблемой, вы можете использовать std :: strstream и операторы >> и << для потоковой передачи примитивных переменных типа C в std :: strings. Это медленно, но легко читается и отлаживается, и очень переносимо. </p>

8 голосов
/ 09 ноября 2009

Что-то вроде кода ниже сделает это. Остерегайтесь проблем, когда sizeof (unsigned int) различается в разных системах, это поможет вам. Для таких вещей лучше использовать типы с четко определенными размерами, например int32_t. Во всяком случае ...

unsigned int x = 123;
char y[3] = {'h', 'i', '\0'};
float z = 1.23f;

// The buffer we will be writing bytes into
unsigned char outBuf[sizeof(x)+sizeof(y)+sizeof(z)];

// A pointer we will advance whenever we write data
unsigned char * p = outBuf;

// Serialize "x" into outBuf
unsigned int32_t neX = htonl(x);
memcpy(p, &neX, sizeof(neX));
p += sizeof(neX);

// Serialize "y" into outBuf
memcpy(p, y, sizeof(y));
p += sizeof(y);

// Serialize "z" into outBuf
int32_t neZ = htonl(*(reinterpret_cast<int32_t *>(&z)));
memcpy(p, &neZ, sizeof(neZ));
p += sizeof(neZ);

int resultCode = send(mySocket, outBuf, p-outBuf, 0);
[...]

... и, конечно, принимающий код будет делать что-то подобное, кроме как в обратном порядке.

1 голос
/ 09 ноября 2009

Это обсуждение , похоже, относится к вашему вопросу, но в нем используется API-интерфейс повышения сериализации

0 голосов
/ 09 ноября 2009

Какова ваша цель? И какие именно средства вы готовы использовать?

Если вы просто хотите выполнить работу с одним конкретным компилятором на одном конкретном компьютере, то самым быстрым и простым, но и самым грязным решением является использование объединения. Вы определяете структуру, в которой ваши элементы являются членами, и объединяете ее с массивом символов. Вы должны сказать компилятору действительно плотно упаковать элементы, что-то вроде #pragma pack (1), и ваша проблема решена. Вы просто сохраняете три значения в элементах, а затем смотрите на них как на массив символов.

Если машина имеет порядок байтов с прямым порядком байтов, и вам нужны порядковые числа с плавающей точкой с большим порядком байтов, вы просто меняете соответствующие символы.

Но есть, по крайней мере, еще дюжина решений, которые приходят на ум, если у вас есть другие цели, такие как переносимость, нестандартный порядок байтов, sizeof (int)! = 4, float, не сохраненный в формате IEEE внутри и т. Д. *

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...