Socket programming: byte and socket byte
When it comes to socket programming, it will certainly be subject to byte sequence conversion.
For a string, there is no difference in the byte order. Just like writing, the memory is like paper, and the string is written from left to right:
Memory Address: 00000000 00000001 00000002 00000003...
Memory Data: 'A' B 'C' D'
All cpu reads are read from left to right.
For multi-byte data (such as short, int, long...), there is a difference between different byte orders.
All x86 architecture CPUs (including x64) use the small-end byte sequence, while the network byte sequence is the large-End sequence:
Int a = 0x01020304;
Memory Address: 00000000 00000001 00000002 00000003...
Large-End sequence a: 0x01 0x02 0x03 0x04: The data read by the cpu is written into the variable from high to low.
Small-End sequence a: 0x04 0x03 0x02 0x01 that is, the data read by the cpu is written from low to high.
What if the program we write does not perform byte sequence conversion:
Int a = 0x01020304;
In the memory of our x86 architecture machine, it is written as 0x04030201 (note that this is the actual writing in the memory, when our program reads data in int type, it will be converted to 0x01020304). At this time, the network is sent, and the send program will be in byte mode (you can think of it as a string ), when one byte is sent to the network, the order is still 0x04030201 (at this time, its order is wrong ). When the data reaches the peer machine, the peer machine also generates one byte of memory, and its order is still 0x04030201. If the peer end is still x86 architecture, it is converted to 0x01020304 again when the data is read, which looks like a correct one. However, if the peer end is a big-end ordered architecture, its int-type data becomes 0x04030201.
Byte order conversion:
Linux provides ready-made functions: htonl, ntohl... this is a series of functions, but only provides 2-byte and 4-byte conversion.
Determine the byte order:
int is_big_endian(void) { int test = 0x12345678; char *p = (char *)&p; if(*p = 0x12) return 1; else return 0;}
Although the function is simple, it is enough.
Byte order conversion macro, which converts numbers into arrays by the way:
#define rhton16(h, n) {(n)[0]=((unsigned short)(h))>>8; (n)[1]=((unsigned short)(h))<<8>>8;}#define rhton32(h, n) {(n)[0]=((unsigned int)(h))>>24; (n)[1]=((unsigned int)(h))<<8>>24; (n)[2]=((unsigned int)(h))<<16>>24; (n)[3]=((unsigned int)(h))<<24>>24;}#define rhton64(h, n) {(n)[0]=((unsigned long)(h))>>56; (n)[1]=((unsigned long)(h))<<8>>56; (n)[2]=((unsigned long)(h))<<16>>56; (n)[3]=((unsigned long)(h))<<24>>56; (n)[4]=((unsigned long)(h))<<32>>56; (n)[5]=((unsigned long)(h))<<40>>56; (n)[6]=((unsigned long)(h))<<48>>56; (n)[7]=((unsigned long)(h))<<56>>56;}
Convert an array to a number:
#define rntoh16(n) (((unsigned short)((n)[1])) | ((unsigned short)((n)[0])<<8))#define rntoh32(n) (((unsigned int)((n)[3])) | ((unsigned int)((n)[2])<<8) | ((unsigned int)((n)[1])<<16) | ((unsigned int)((n)[0])<<24))#define rntoh64(n) (((unsigned long)((n)[7])) | ((unsigned long)((n)[6])<<8) | ((unsigned long)((n)[5])<<16) | ((unsigned long)((n)[4])<<24) | ((unsigned long)((n)[3])<<32) | ((unsigned long)((n)[2])<<40) | ((unsigned long)((n)[1])<<48) | ((unsigned long)((n)[0])<<56))