网络字节序与主机字节序的相互转换
1.網(wǎng)絡(luò)字節(jié)序與主機(jī)字節(jié)序
在Linux網(wǎng)絡(luò)編程中,經(jīng)常碰到網(wǎng)絡(luò)字節(jié)序與主機(jī)字節(jié)序的相互轉(zhuǎn)換。說(shuō)到網(wǎng)絡(luò)字節(jié)序與主機(jī)字節(jié)序需要清晰了解以下幾個(gè)概念。
字節(jié)序,顧名思義,指字節(jié)在內(nèi)存中存儲(chǔ)的順序。比如一個(gè)int32_t類型的數(shù)值占用4個(gè)字節(jié),這4個(gè)字節(jié)在內(nèi)存中的排列順序就是字節(jié)序。字節(jié)序有兩種:
(1)小端字節(jié)序(Little endinan),數(shù)值低位存儲(chǔ)在內(nèi)存的低地址,高位存儲(chǔ)在內(nèi)存的高地址;
(2)大端字節(jié)序(Big endian),數(shù)值高位存儲(chǔ)在內(nèi)存的低地址,低位存儲(chǔ)在內(nèi)存的高地址。
下面以32位位寬數(shù)值0x12345678為例,小端字節(jié)序與大端字節(jié)序具體的存儲(chǔ)區(qū)別如下所示:
主機(jī)字節(jié)序,即CPU存儲(chǔ)數(shù)據(jù)時(shí)采用的字節(jié)順序。不同的CPU設(shè)計(jì)時(shí)采用的字節(jié)序是不同的,談到字節(jié)序的問(wèn)題,必然牽涉到兩大CPU派系。那就是Motorola的PowerPC系列CPU和Intel的x86與x86_64(該指令集由AMD率先設(shè)計(jì)推出)系列CPU。PowerPC系列采用big endian方式存儲(chǔ)數(shù)據(jù),而x86與x86_64系列則采用little endian方式存儲(chǔ)數(shù)據(jù)。平常大多數(shù)PC與服務(wù)器如果使用的是Intel與AMD CPU,一般都是little endian。
如何具體判斷本機(jī)的主機(jī)字節(jié)序呢?參考如下代碼:
//@ret:返回0小端字節(jié)序,返回1大端字節(jié)序 int dGetHostByteOrder() {uint32_t a = 0x12345678; uint8_t *p = (uint8_t *)(&a); if(*p==0x78){return 0}else{return 1;} }網(wǎng)絡(luò)字節(jié)序,是TCP/IP中規(guī)定好的一種數(shù)據(jù)表示格式,它與具體的CPU類型、操作系統(tǒng)等無(wú)關(guān),從而可以保證數(shù)據(jù)在不同主機(jī)之間傳輸時(shí)能夠被正確解釋。網(wǎng)絡(luò)字節(jié)順序采用big endian排序方式。
2.網(wǎng)絡(luò)字節(jié)序與主機(jī)字節(jié)序的相互轉(zhuǎn)換
2.1常用系統(tǒng)調(diào)用
Linux socket網(wǎng)絡(luò)編程中,經(jīng)常會(huì)使用下面四個(gè)C標(biāo)準(zhǔn)庫(kù)函數(shù)進(jìn)行字節(jié)序間的轉(zhuǎn)換。
#include <arpa/inet.h>uint32_t htonl(uint32_t hostlong); //把uint32_t類型從主機(jī)序轉(zhuǎn)換到網(wǎng)絡(luò)序 uint16_t htons(uint16_t hostshort); //把uint16_t類型從主機(jī)序轉(zhuǎn)換到網(wǎng)絡(luò)序 uint32_t ntohl(uint32_t netlong); //把uint32_t類型從網(wǎng)絡(luò)序轉(zhuǎn)換到主機(jī)序 uint16_t ntohs(uint16_t netshort); //把uint16_t類型從網(wǎng)絡(luò)序轉(zhuǎn)換到主機(jī)序2.2 64位數(shù)值的轉(zhuǎn)換
現(xiàn)在如果需要對(duì)64位類型數(shù)據(jù)進(jìn)行主機(jī)字節(jié)序與網(wǎng)絡(luò)字節(jié)序的轉(zhuǎn)換,沒(méi)有現(xiàn)成系統(tǒng)API可用,可以通過(guò)下面兩種方法進(jìn)行轉(zhuǎn)換:
###2.2.1使用移位
//主機(jī)序轉(zhuǎn)網(wǎng)絡(luò)序 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; } } //網(wǎng)絡(luò)序轉(zhuǎn)主機(jī)序 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使用聯(lián)合體union
根據(jù)聯(lián)合體的特性:聯(lián)合中所有成員引用的是內(nèi)存中相同的位置,其長(zhǎng)度為最長(zhǎng)成員的長(zhǎng)度。
typedef struct { unsigned int u32_h; unsigned int u32_l; }Int64_t; typedef union { unsigned long long u64; Int64_t st64; }Convert64_t;//主機(jī)序轉(zhuǎn)網(wǎng)絡(luò)序 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;} }//網(wǎng)絡(luò)序轉(zhuǎn)主機(jī)序 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使用編譯器內(nèi)置函數(shù)
#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參考資料
[1]網(wǎng)絡(luò)字節(jié)序與主機(jī)字節(jié)序 高低位
[2]htonl(3) - Linux man page
[3]如何在C ++中的big-endian和little-endian值之間進(jìn)行轉(zhuǎn)換?
總結(jié)
以上是生活随笔為你收集整理的网络字节序与主机字节序的相互转换的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: android 屏幕统计,OffScre
- 下一篇: python实现人工智能识别水果