开发者

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

开发者 https://www.devze.com 2022-12-11 10:48 出处:网络
I want to transmit data over the network, but I don\'t want to use any foreign libraries (Standard C/C++ is ok).

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 f开发者_C百科or my program as for me to learn from.


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.


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.


This discussion seems relevant to your question, but it uses boost serialization API


What exactly is your goal? And what exactly are the means you're willing to use?

If you just want to get the job done with one particular compiler on one particular computer, then the fastest and easiest, but also dirtiest, solution is, to use a union. You define a struct that has your items as members and merge that with the character array. You need to tell the compiler to pack the members really tightly, something along the lines of #pragma pack(1), and your problem is solved. You just store the three values in the members, and then look at it as a character array.

If the machine is little endian, and you need big endian ints / floats, you just swap the relevant characters.

But there are at least another dozen solutions that come to mind if you have other goals, like portability, non-standard byte order, sizeof(int) !=4, float not stored in IEEE format internally, etc.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号