網絡字節序與主機字節序的相互轉換
1.網絡字節序與主機字節序
在Linux網絡編程中,經常碰到網絡字節序與主機字節序的相互轉換。說到網絡字節序與主機字節序需要清晰了解以下幾個概念。
字節序,顧名思義,指字節在內存中存儲的順序。比如一個int32_t類型的數值占用4個字節,這4個字節在內存中的排列順序就是字節序。字節序有兩種:
(1)小端字節序(Little endinan),數值低位存儲在內存的低地址,高位存儲在內存的高地址;
(2)大端字節序(Big endian),數值高位存儲在內存的低地址,低位存儲在內存的高地址。
下面以32位位寬數值0x12345678為例,小端字節序與大端字節序具體的存儲區別如下所示:
主機字節序,即CPU存儲數據時采用的字節順序。不同的CPU設計時采用的字節序是不同的,談到字節序的問題,必然牽涉到兩大CPU派系。那就是Motorola的PowerPC系列CPU和Intel的x86與x86_64(該指令集由AMD率先設計推出)系列CPU。PowerPC系列采用big endian方式存儲數據,而x86與x86_64系列則采用little endian方式存儲數據。平常大多數PC與服務器如果使用的是Intel與AMD CPU,一般都是little endian。
如何具體判斷本機的主機字節序呢?參考如下代碼:
//@ret:返回0小端字節序,返回1大端字節序 int dGetHostByteOrder() { uint32_t a = 0x12345678; uint8_t *p = (uint8_t *)(&a); if(*p==0x78) { return 0 } else { return 1; } }
網絡字節序,是TCP/IP中規定好的一種數據表示格式,它與具體的CPU類型、操作系統等無關,從而可以保證數據在不同主機之間傳輸時能夠被正確解釋。網絡字節順序采用big endian排序方式。
2.網絡字節序與主機字節序的相互轉換
2.1常用系統調用
Linux socket網絡編程中,經常會使用下面四個C標準庫函數進行字節序間的轉換。
#include <arpa/inet.h> uint32_t htonl(uint32_t hostlong); //把uint32_t類型從主機序轉換到網絡序 uint16_t htons(uint16_t hostshort); //把uint16_t類型從主機序轉換到網絡序 uint32_t ntohl(uint32_t netlong); //把uint32_t類型從網絡序轉換到主機序 uint16_t ntohs(uint16_t netshort); //把uint16_t類型從網絡序轉換到主機序
2.2 64位數值的轉換
現在如果需要對64位類型數據進行主機字節序與網絡字節序的轉換,沒有現成系統API可用,可以通過下面兩種方法進行轉換:
###2.2.1使用移位
//主機序轉網絡序 unsigned long long htonll(unsigned long long val) { if(__BYTE_ORDER == __LITTLE_ENDIAN) { return (((unsigned long long )htonl((int)((val << 32) >> 32))) << 32) | (unsigned int)htonl((int)(val >> 32)); } else if (__BYTE_ORDER == __BIG_ENDIAN) { return val; } } //網絡序轉主機序 unsigned long long ntohll(unsigned long long val) { if (__BYTE_ORDER == __LITTLE_ENDIAN) { return (((unsigned long long )ntohl((int)((val << 32) >> 32))) << 32) | (unsigned int)ntohl((int)(val >> 32)); } else if (__BYTE_ORDER == __BIG_ENDIAN) { return val; } }
2.2.2使用聯合體union
根據聯合體的特性:聯合中所有成員引用的是內存中相同的位置,其長度為最長成員的長度。
typedef struct { unsigned int u32_h; unsigned int u32_l; }Int64_t; typedef union { unsigned long long u64; Int64_t st64; }Convert64_t; //主機序轉網絡序 unsigned long long htonll(unsigned long long val) { if (__BYTE_ORDER == __LITTLE_ENDIAN) { Convert64_t box_in, box_out; box_in.u64 = val; box_out.st64.u32_h = htonl(box_in.st64.u32_l); box_out.st64.u32_l = htonl(box_in.st64.u32_h); return box_out.u64; } else if (__BYTE_ORDER == __BIG_ENDIAN) { return val; } } //網絡序轉主機序 unsigned long long ntohll(unsigned long long val) { if (__BYTE_ORDER == __LITTLE_ENDIAN) { Convert64_t box_in, box_out; box_in.u64 = val; box_out.st64.u32_h = ntohl(box_in.st64.u32_l); box_out.st64.u32_l = ntohl(box_in.st64.u32_h); return box_out.u64; } else if(__BYTE_ORDER == __BIG_ENDIAN) { return val; } }
2.2.3使用編譯器內置函數
#ifdef WIN32 #define ntohll(x) _byteswap_uint64 (x) #define htonll(x) _byteswap_uint64 (x) #else #if __BYTE_ORDER == __BIG_ENDIAN #define ntohll(x) (x) #define htonll(x) (x) #else #if __BYTE_ORDER == __LITTLE_ENDIAN #define ntohll(x) __bswap_64 (x) #define htonll(x) __bswap_64 (x) #endif #endif #endif
浙公網安備 33010602011771號