Byte orders!

Started by
8 comments, last by md_lasalle 17 years, 6 months ago
Hi, I wanna make sure my network librarie stays as cross platform as possible... right now, the user of the library pass his structs by casting it like this : (char*)&SomeStruct does the user needs to manually set to network byte order before the cast, or can i change de byte order directly on the char array for him ? if so, how ? and since the functions are htons(),htonl() what about floats ? will htonl work here since both are on 32 bit ??? Thanks for any advice
Advertisement
AFAIK:

byte ordering should be set to a specific standard for network transport. so just pick either big or little endian. Then, for your difference compile platforms do the following:

Let's say you pick little endian.

On little endian platforms your network library will do no byte ordering translation

On big endian platforms your network will convert byte ordering both before sending and after receiving packets.

But like i said, "AFAIK". Been a really long time since i've dealt with network anything.

-me
If you have a struct like this:

struct{
int a;
int b;
char c;
float f;
};

You cant just order the entire struct in one go. ie sending (char*)&MyStruct, You have to order first the bytes of a, then b, then c, then f etc. The byte ordering wont know the size of each member in a struct, so the byteordering will get


123456789 10 11 14 13 14

is not the same as
14 13 12 11 10 987654321

it should be
(int)4321
(int)8765
(char)10 9
(float) 14 13 12 11

4321-8765-8765-10 9-14 13 12 11

ordering the entire memory of the struct would result in:
14 13 12 11-10 987-65-4321

Atleast thats how i think it is, hmm, maybe i remember wrong.. =)


AFAIK you should be able to cast a float pointer to a long pointer and use htonl to order its bytes correctly.


Shields up! Rrrrred alert!
The user does not set the network byte order. "Network byte order" is the name for the byte order that is the standard for network communications. Theoretically you should never need to know whether that order happens to be little endian or big endian, and all your code should work fine regardless of which one it really is.

If you're on a system whose byte order agrees with network byte order, htons and htonl will do nothing at all. If you're not, they'll switch.

You can in fact directly change the byte order on the character array. There's a good little explanation of how here: http://www.codeproject.com/cpp/endianness.asp

htonl() will work fine on any 32-bit value.

(P.S. If you really have to know, network byte order is big-endian. Shhh).
Quote:Original post by Ozymandias42
You can in fact directly change the byte order on the character array.


but if you look at peter_b's reply, it would seem logical that the result would be different if i reverse the byte order on the char array directly...

anyway after i read your article, i can test if the current system is Big-Endian, and could do the byte reversing when i receive the packet...since the librari will be more used on Windows/Linux systems, only macs could have the small performance impact...

What do you guys think ?

The byte swapping is not a performance concern, don't try to "optimize" it.

The other reason you should not cast your objects to char pointers, byte swap them, and send them is that that might send over icky junk like the vtable pointer, pad bytes, et cetera, that you don't want.

Serialize and deserialize on a memberwise basis, and use whatever hton* function is appropriate, and don't be concerned about it being inefficient.
If you're really worried about performance, nobody said you HAVE to use network byte order. You could use little-endianness, and then when you port your program elsewhere, you could put in a switch TO big endian from little. I recommend sticking with network byte order, though
Quote:htonl() will work fine on any 32-bit value.


There are implementations of htonl() that don't work on floats. They look similar to this:

inline unsigned int htonl(unsigned int v) {  return (v>>24) | ((v>>8)&0xff00) | ((v<<8)&0xff0000) | (v<<24);}


For htonl() to be "safe" for floats, you have to do type punning:

  float f = 3.14;  float swapped_f;  *(unsigned int *)&swapped_f = htonl(*(unsigned int *)&f);


Which, in turn, may confuse some highly optimized compilers. If you really care, you should write your own swapping functions that go through char*, because type-analysis compilers must assume that a char* can alias to any type.
enum Bool { True, False, FileNotFound };
Assuming you are targetting PC's I'm going to buck the trend and say you should use little-endian unless you are implementing a standard protocol that already uses big-endian (i.e. network order). It's simpler (because you don't need to convert) and now that Apple has fallen to the Intel dark side it's also becoming universal. Of course if you are targetting consoles or other devices the choice may not be so clear-cut.

The important thing is not so much what order you send stuff in, but that that order be well-defined. "everything is in little-endian" is just as well defined as "everything is in big-endian". There are other options as well, e.g. DCE RPC sends data in (for lack of a better term) "sender-endian". i.e. the data is in whatever format the sender uses natively and there is a flag somewhere that says what that format is.
-Mike
thank you all for your replies! I'll considere checking the Endianess of the current system, if its little : do nothing, if its big : swap byte on receive and swap bytes before send

This topic is closed to new replies.

Advertisement