簡介
基於X86平台的PC機是小端
位元組序的,而有的嵌入式平台則是
大端位元組序的。因而對int、uint16、uint32等多於1位元組類型的數據,在這些嵌入式平台上應該變換其存儲順序。通常我們認為,在空中傳輸的位元組的順序即網路位元組序為標準順序,考慮到與協定的一致以及與同類其它平台產品的互通,在程式中發數據包時,將
主機位元組序轉換為
網路位元組序,收數據包處將網路位元組序轉換為
主機位元組序。
跨平台
在本LINUX的書里介紹到INTEL的CPU使用的小端
位元組序其他比MOTOROLA
68000系列CPU使用的是
大端位元組序如果不轉換 將數據通過網路發出時 比如MOTOROLA發一個16位數據:0X1234 傳送到INTEL時
就被INTEL解釋為0X3412 也就是4660成了13330 所以有時候需要一些函式來進行大小端位元組序的轉換
概念
記憶體里是具體怎么存放的形式?為什麼會有CPU解釋的不同?數據不是按12345678……這樣的順序一直排列的么?
如: 一個多位元組值 0xFECDBA98,記憶體從地址100開始存放
降序: FE 對應地址100 | CD 對應地址 101 | BA 對應地址 102 | 98 對應地址 103
升序: 98 | BA | CD | FE ---->;same above
注意,我們的書寫字數據表示法是從高位元組位--->低位元組位(從左到右)
記憶體地址生長方向為: 從左到右 由低到高(這是不變的)
數據為: 0x98BADCFE
驗證
更簡單的調用VC里的checkEndian()
Intel處理器的位元組順序是和DEC VAX處理器的位元組順序一致的。因此它與68000型處理器以及Internet的順序是不同的,所以用戶在使用時要特別小心以保證正確的順序。
任何從Windows Sockets函式對
IP位址和
連線埠號的引用和傳送給Windows Sockets函式的IP位址和連線埠號均是按照網路順序組織的,這也包括了
sockaddr_in結構這一數據類型中的IP位址域和連線埠域(但不包括sin_family域)。
案例1
如何手算主機位元組順序轉換為網路位元組順序?
假設某16位的整數,主機位元組順序的值是21,那么它的網路位元組順序是多少?
解決的步驟是:
1、將21化成二進制,二進制,如果不足16位就在其前面補0,補滿16位。 21轉換成二進制是:10101,在它前面補0,補滿16位後就得到:
00000000#00010101
2、將這個16位二進制字元平分成兩段,每段8位
0000000 000010101 == > 00000000#00010101
3、顛倒這兩段的順序,然後去掉第一個字元“1”前面的0,化成十進制就得到了網路位元組順序的值了。 00000000#00010101顛倒後:00010101#00000000
即00 01 01 01 00 000000
去掉第一個“1”前面的0得到:10 10 10 00 00 00 0
化成十進制得到:5376
通過這個程式可以進行驗證:
#include<stdio.h>#include<string.h>#include<netdb.h>intmain(void){structservent*server=NULL;charsername[16]="\0";charproname[4]="\0";printf("請輸入服務名稱:");gets(sername);printf("請輸入使用的協定:");gets(proname);//如果能夠通過getserbyname函式找到結果,就輸出結果,否則報錯if((server=getservbyname(sername,proname))!=NULL){printf("網路服務連線埠:\n");printf("網路位元組順序值:%d\n",server->s_port);printf("主機位元組順序值:%d\n",ntohs(server->s_port));}else{printf("該服務不存在\n");}return0;}
編譯後,按提示輸入ftp和tcp,就得到網路位元組順序值是5376,主機位元組順序值是21。
16位的轉換函式:ntohs和htons
ntohs(network to host short)是將網路位元組順序轉換為主機位元組順序,返回值是一個16位的整數,即2個位元組長度的整數(1位元組=8位)short int,也可以寫作uint16_t。
htons(host to network short)是將主機位元組順序轉換為網路位元組順序,返回值也是一個16位的整數short int。 32位轉換函式ntohl和htonl。
ntohl(network to host long)是將網路位元組順序轉換為主機位元組順序,返回值是一個16位的整數,即2個位元組長度的整數(1位元組=8位)long int,也可以寫作uint32_t。
htonl(host to network long)是將主機位元組順序轉換為網路位元組順序,返回值也是一個16位的整數long int。
IP位址使用的是32位的無符號整數,所以,在對IP位址進行處理的時候,就需要用到32位的轉換了。
一個點分十進制的IP位址是192.168.0.1,還原成原來的二進制原碼是:
11000000 10101000 0000000 00000 0001
這是主機字元順序存儲的值。
將其按照每個位元組分隔開來,即每8位分隔開來,得到:
11000000 10101000 00000000 00000001
將這4段二進制編碼進行完全顛倒,就得到了網路位元組順序:
00000001000000001010100011000000
去掉第一個字元“1”前面無效的0,得到:
10 00 00 00 01 01 01 00 01 10 00 00 0 化成十進制得到:16820416inet_addr函式可以將一個點分十進制的IP位址轉換成網路位元組順序的長整形十進制值。同樣的,inet_ntoa函式將網路位元組順序的長整形十進制數值的IP位址轉換成點分十進制。
案例2
將一個點分十進制的IP位址轉換成它的二進制原碼
分析:用inet_addr函式,轉換點分十進制的IP為網路位元組順序的長整型十進制數值後,再用ntohl函式將其轉換成主機位元組順序的長整型整數,然後使用一個自定義函式將這個主機位元組順序的長整形整數轉換成二進制。
#include<stdio.h>#include<sys/socket.h>#include<netdb.h>#include<arpa/inet.h>#include<netinet/in.h>#include<string.h>//十進制轉二進制函式,參數為無符號超長整形(防止溢出)voiddec(constunsignedlonglongintx){if(x/2>0){dec(x/2);printf("%d",x%2);}else{printf("%d",x);}}intmain(void){charipaddr[15]="\0";unsignedlonglonginthostaddr=0;//主機位元組IP的值printf("請輸入IP位址:");gets(ipaddr);hostaddr=ntohl(inet_addr(ipaddr));printf("IP位址的網路位元組順序的值:%ld\n",inet_addr(ipaddr));//調用dec函式,為了防止溢出將hostaddr強制轉換成無符號超長整形後再轉換成二進制printf("二進制形式是:\n");dec((unsignedlonglongint)hostaddr);printf("\n");return0;}