Jump to content
  • Advertisement
Sign in to follow this  
catch

Byte order and Endian Concerns

This topic is 4889 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

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

Share this post


Link to post
Share on other sites
Advertisement
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.

Share this post


Link to post
Share on other sites
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:


//******************************************************
// $$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*)&in_f )[ 0 ];
m_pData[ m_iPos++ ] = ( (unsigned char*)&in_f )[ 1 ];
m_pData[ m_iPos++ ] = ( (unsigned char*)&in_f )[ 2 ];
m_pData[ m_iPos++ ] = ( (unsigned char*)&in_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

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

Participate in the game development conversation and more when you create an account on GameDev.net!

Sign me up!