Byte order and Endian Concerns
Folks, I am writing a little client/server app that I would like to be cross platform (at some point). I am currently using wxWidgets (2.5.3) for the gui elements and decided since it had sockets wrapped up in it, it would suffice fine for now. My question is about host/network byte ordering and endianness. Since I do want to be crossplatform, I have a concern. Sending integral values over sockets that are greater than one byte. So I'm reading some articles here on game dev and came across this page: clicky Very good page, and section 3.1 talks about converting bytes, but I'm a little unclear on something. If I want to send an int over the network (message codes/packet headers so I know how to sort incoming data), I need to convert using these functions in this article before sending? So, on client -> server transmissions, I convert my int from host to network, then send that value? Then, on server reception of the client packet, I convert the header value (the int) from Network Byte Order to host byte order? The basic point of all of this is: I can't remain cross platform by carelessly sending ints or other larger than one byte values over the network, correct? They *need* to be converted by me? Secondly, anyone know if this is a speed concern doing this on the fly every call, or should I cache the converted values so I don't convert them every single time (but take up more memory...) Anyhow, I'm new to network programming, so any responses will help lots. Thanks in advance, Catch
"Creativity requires you to murder your children." - Chris Crawford
If I understand correctly, you are worried about an int of value 1 sent over the network coming up as '00 00 00 01' or '01 00 00 00' right? My suggestion is to have the server program test which format the machine uses that has the server currently running on it (write the int to a byte buffer and check which value is 01?) then provide this information to any client trying to connect. The client program will also test which format the machine that it is on uses, and will convert or not convert the data as needed. Assuming the clients are not as time critical as the server, you can just have the clients do the converting if needed, and all the information sent to the server should be in its correct format.
I am by no means an expert and am just making things up, so if this doesn't make sense/won't work... ignore me.
I am by no means an expert and am just making things up, so if this doesn't make sense/won't work... ignore me.
Did you look into the standard ntohl(), htonl(), htons() and ntohs() calls?
Otherwise, you might want to make your own helper class like the one here:
-cb
Otherwise, you might want to make your own helper class like the one here:
//****************************************************** // $$C sBuffer // // General linear buffer manupulation. It bears no // storage (uses external space), but keeps track of // the data position and size in the buffer. // //******************************************************class sBuffer{ public: //---- General sBuffer( unsigned char * ); sBuffer( char * ); sBuffer( void * ); ~sBuffer( ); //---- Encode. void WriteByte( unsigned char ); void WriteShort( short ); void WriteLong( long ); void WriteFloat( float ); void WriteString( char * ); void WriteMulti( void *, int ); //---- Decode. unsigned char ReadByte( void ); short ReadShort( void ); long ReadLong( void ); float ReadFloat( void ); char * ReadString( void ); void ReadMulti( void *, int ); public: unsigned char * m_pData; int m_iPos;};inline sBuffer::sBuffer( unsigned char * in_str ) : m_pData( in_str ) , m_iPos(0){}inline sBuffer::sBuffer( char * in_str ) : m_pData( (unsigned char*) in_str ) , m_iPos(0){}inline sBuffer::sBuffer( void * in_str ) : m_pData( (unsigned char*) in_str ) , m_iPos(0){}inline sBuffer::~sBuffer(){}inline void sBuffer::WriteByte( unsigned char in_ch ){ m_pData[ m_iPos++ ] = in_ch;}inline void sBuffer::WriteShort( short in_s ){ m_pData[ m_iPos++ ] = (unsigned char) ( (in_s >> 8 ) & 0xFF ); m_pData[ m_iPos++ ] = (unsigned char) ( (in_s ) & 0xFF );}inline void sBuffer::WriteLong( long in_l ){ m_pData[ m_iPos++ ] = (unsigned char) ( (in_l >> 24 ) & 0xFF ); m_pData[ m_iPos++ ] = (unsigned char) ( (in_l >> 16 ) & 0xFF ); m_pData[ m_iPos++ ] = (unsigned char) ( (in_l >> 8 ) & 0xFF ); m_pData[ m_iPos++ ] = (unsigned char) ( (in_l ) & 0xFF );}inline void sBuffer::WriteFloat( float in_f ){ m_pData[ m_iPos++ ] = ( (unsigned char*)∈_f )[ 0 ]; m_pData[ m_iPos++ ] = ( (unsigned char*)∈_f )[ 1 ]; m_pData[ m_iPos++ ] = ( (unsigned char*)∈_f )[ 2 ]; m_pData[ m_iPos++ ] = ( (unsigned char*)∈_f )[ 3 ];}inline void sBuffer::WriteString( char * in_str ){ while ( *in_str ) m_pData[ m_iPos++ ] = (unsigned char) *in_str++; m_pData[ m_iPos++ ] = 0;}inline void sBuffer::WriteMulti( void * in_pData, int in_iLen ){ ::memmove( &m_pData[ m_iPos ], in_pData, in_iLen ); m_iPos += in_iLen;}inline unsigned char sBuffer::ReadByte( void ){ return( m_pData[ m_iPos++ ] );}inline short sBuffer::ReadShort( void ){ short s; s = (short) m_pData[ m_iPos++ ]; s = (s << 8) + (short) m_pData[ m_iPos++ ]; return( s );}inline long sBuffer::ReadLong( void ){ long l; l = (long) m_pData[ m_iPos++ ]; l = (l << 8) + (long) m_pData[ m_iPos++ ]; l = (l << 8) + (long) m_pData[ m_iPos++ ]; l = (l << 8) + (long) m_pData[ m_iPos++ ]; return( l );}inline float sBuffer::ReadFloat( void ){ float f; ( (unsigned char*)&f )[ 0 ] = m_pData[ m_iPos++ ]; ( (unsigned char*)&f )[ 1 ] = m_pData[ m_iPos++ ]; ( (unsigned char*)&f )[ 2 ] = m_pData[ m_iPos++ ]; ( (unsigned char*)&f )[ 3 ] = m_pData[ m_iPos++ ]; return( f );}inline char * sBuffer::ReadString( void ){ char * l_pStr = (char*) &m_pData[ m_iPos ]; while( m_pData[ m_iPos ] ) m_iPos ++; m_iPos ++; return( l_pStr );}inline void sBuffer::ReadMulti( void * in_pData, int in_iLen ){ ::memmove( in_pData, &m_pData[ m_iPos ], in_iLen ); m_iPos += in_iLen;}
-cb
you have the right idea. convert from host to network byte order before sending, and when you receive, convert from network byte order back to host. im not expert, but this doesnt seem like it will be very costly in terms of CPU usage. cache'ing seems like a waste.
also, keep in mind (IIRC) that this only effects you if you want to be able to communicate with a PC using Mac, and vice versa.
also, keep in mind (IIRC) that this only effects you if you want to be able to communicate with a PC using Mac, and vice versa.
FTA, my 2D futuristic action MMORPG
There are two basic approaches to this problem. The first is to always convert numbers to have a standard format on the wire. That's what all the stuff about "network byte order" is talking about. The standard socket functions ntohs, ntohl, htons, and htonl will convert numbers to be big endian on the wire.
If you want you can force numbers to on the wire to be little endian (Windows is little-endian) but you have to write your own conversion functions (assuming you care about talking to big-endian machines).
The other method is to not have a standard network byte order and instead early on you tell the other side what format you're sending in and they can convert or not as they need. This isn't as common but it is done. The goal of this method is to optimize for the case of two machines with the same endianness talking to each other. It comes at the cost of slightly more complicated code on the reciever side though.
If you want you can force numbers to on the wire to be little endian (Windows is little-endian) but you have to write your own conversion functions (assuming you care about talking to big-endian machines).
The other method is to not have a standard network byte order and instead early on you tell the other side what format you're sending in and they can convert or not as they need. This isn't as common but it is done. The goal of this method is to optimize for the case of two machines with the same endianness talking to each other. It comes at the cost of slightly more complicated code on the reciever side though.
-Mike
This topic is closed to new replies.
Advertisement
Popular Topics
Advertisement
Recommended Tutorials
Advertisement