Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Serialize Strings, ints and floats to character arrays for networking WITHOUT LIBRARIES

I want to transmit data over the network, but I don't want to use any foreign libraries (Standard C/C++ is ok).

for example:

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

I want this in an

char xyz[11];

array.

Note: To transmit it over network, I need Network Byte order for the unsigned int (htonl function), then I need to somehow serialize the float to be in IEEE 754 form (theres many functions on the internet), and I know it.

How do I get them into the the xyz-Array, nicely lined up end to end, so I can use this as a buffer for my socket + send() function? Obviously I have reverse functions (ntohl, and a reverse IEEE 754) to get them out but I need a technique there too, preferably the same...

It would be something like this:

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   -

How can I accomplish this without external libraries and minimal use of standard library functions? This isn't so much for my program as for me to learn from.

like image 561
wsd Avatar asked Nov 09 '09 19:11

wsd


2 Answers

Ah, you want to serialize primitive data types! In principle, there are two approaches: The first one is, that you just grab the internal, in-memory binary representation of the data you want to serialize, reinterpret it as a character, and use that as you representation:

So if you have a:

double d;

you take the address of that, reinterpret that pointer as a pointer to character, and then use these characters:

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

This works with all primitive data types. The main problem here is, that the binray representation is implementation dependent (mainly CPU dependent). (And you will run into subtle bugs when you try doing this with IEEE NANs...).

All in all, this approach is not portable at all, as you have no control at all over the representation of your data.

The second approach is, to use a higher-level representation, that you yourself have under control. If performance is not an issue, you could use std::strstream and the >> and << operators to stream primitive C type variables into std::strings. This is slow but easy to read and debug, and very portable on top of it.

like image 157
Carsten Kuckuk Avatar answered Nov 15 '22 20:11

Carsten Kuckuk


Something like the code below would do it. Watch out for problems where sizeof(unsigned int) is different on different systems, those will get you. For things like this you're better off using types with well-defined sizes, like int32_t. Anyway...

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);
[...]

... and of course the receiving code would do something similar, except in reverse.

like image 24
Jeremy Friesner Avatar answered Nov 15 '22 19:11

Jeremy Friesner