I'm still trying to learn my way through send and recv with sockets in C. Now, I have a struct that I want to send over the socket.

struct myPacket {
	int a;
	double b;
	double c;
	int d;
	char e[80];
};

I have a client on a big-endian machine and a server on a little-endian machine. My goal at the moment is to be able to fill in the values of the struct using scanf (got that done) and then send the struct to the server and have the server print out the values for A,B,C,D, and E. I'm struggling in trying to find a way to serialize the struct and then deserialize it on the server side.

I found in another forum the suggestion to use memcpy to get it into an unsigned char array.

struct myPacket msg;
unsigned char buffer[sizeof(msg)];
memcpy(&buffer, &msg, sizeof(msg));
send(sockfd,buffer,sizeof(buffer),0);

I'm not sure how to undo this process on the server side, however. I tried this:

struct myPacket *msg;
struct myPacket theMsg;
retval = recv(clientfd, buffer, 112, 0);
msg = (struct myPacket *)&buffer;
theMsg = *msg;

And was able to printf the contents of theMsg.a successfully (after doing an ntohl(theMsg.a) to account for the byte order), however the rest of the contents were garbage (I am switching the byte order - still garbage).

I would appreciate some help!

Thanks,
--Dan

Salem commented: Nice question! - thanks +20

I should also mention that I'm not tied to the way I'm serializing the struct. My ultimate goal is to take those five pieces of data (the two ints, the two doubles, and the char array) and send them all over the socket with one send statement. If there's a better way to do it, by all means, let me know!

> I have a client on a big-endian machine and a server on a little-endian machine.
And that's the least of your worries.

struct myPacket {
	int a; // 1
	// 2
	double b; // 3
	double c;
	int d;
	char e[80];
};

1. As well as endian-ess, how big are integers on both machines?
The standard only states minimum sizes for types.
Saying "short int" or "long int" is an improvement, since most machines which are network capable and have implemented htons() and these are generally understood to be 16 and 32 bits respectively.

2. Padding and alignment.
The compiler is free to insert padding anywhere in a struct (except before the first element) to enable efficient access to the struct members. For example, if your source machine needs a double to be on an 8-byte boundary, then you might have
- 4 byte integer
- 4 byte hole
- 8 byte double
- 8 byte double
- 4 byte integer
- 80 characters.

Your destination machine might not care about alignment of doubles (or 4 is good enough) and have
- 4 byte integer
- 8 byte double
- 8 byte double
- 4 byte integer
- 80 characters.

Copying one structure over the other results in garbage.


3. Representation of floats.
The really horrible news is there are a plethora of floating point representations.
But lets assume for the moment that both machines are using regular IEEE 754 format for floating point. In which case all you have to do is write your "htons" for double.

char temp[sizeof(double)];
memcpy( temp, &myDouble, sizeof(myDouble) );
// now reverse the bytes, store in a message and send.

Solutions?
XDR (as mentioned) is one way, especially if you have a lot of different structs to send.

A simple way (for just 1 or 2 structs) would be to just sprintf() each member to a char array, with separators. The down side to this is that you could lose information in printing doubles. An alternative to printing doubles would be to print the hex string of the bytes which represent the double - it's more work, but more reliable (assuming IEEE754 at both ends).

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.