1. 程式人生 > >主機位元組序與網路位元組序

主機位元組序與網路位元組序

1 主機位元組序

主機位元組序(host-byte)指的是處理器儲存資料的位元組順序。對於Inter x86處理器來說,將資料的不重要的部分儲存在低地址,重要的部分儲存在高地址,即低地址中儲存的是資料的低位元組位,高地址儲存的是資料的高位元組位。

int ip_Address_hostbyte = 0x12345678;

此時,Inter x86處理器按照如圖1的格式來儲存變數ip_Address_hostbyte。

圖1 ip_Address_hostbyte的儲存格式

從圖1中可以看出,變數ip_Address_hostbyte是按照主機位元組序的方式被儲存的。如果將低地址看成是“頭”,主機位元組序又叫做“小頭”(little-endian)。

2 網路位元組序

網路位元組序(network-byte)指的是網路程式設計時,儲存資料的位元組順序。與“主機位元組序”相反,網路位元組序將資料的重要部分儲存在低地址,不重要的部分儲存到高地址,即低地址儲存的是資料的高位元組位,高地址儲存的是資料的低位元組位。在網路程式設計時用到的IP地址和埠號都需要網路位元組序。

3 轉換函式

可以通過一系列的函式實現主機位元組序和網路位元組序之間的轉換。

3.1 主機位元組序轉換成網路位元組序

可以通過htonl()、htons()、WSAHtonl()和WSAHtons()函式實現主機位元組序轉換成網路位元組序。函式中的h表示主機host,n表示網路networ,l表示長整形long,s表示短整形short。

3.1.1 htonl()/htons()

htonl()函式的格式為

u_long htonl(u_long hostlong);

其中,引數hostlong是主機位元組序的資料,該函式的返回值是轉換之後的網路位元組序的資料。

int ip_Address_networkbyte = htonl(ip_Address_hostbyte);

其中,ip_Address_hostbyte是“1 主機位元組序”中定義的變數。轉換後的網路位元組序變數ip_Address_network的資料儲存格式如圖2所示。

圖2 ip_Address_network的儲存格式

從圖2中可以看出,如果將低地址看成“頭”的話,網路位元組序又可以叫做“大頭”(big-endian)。

htons()函式的格式為

u_short htons(u_short hostshort);

該函式的用法與htonl()類似。

3.1.2 WSAHtonl()/WSANtohl()

WSAHtonl()/WSANtohl()是Winsock版本的轉換函式。

WSAHtonl()函式的格式為

int WSAHtonl(SOCKET s, u_long hostLong, u_long* LpnetLong);

其中,引數s是套接字的描述符;hostLong是要轉換的主機位元組序的長整形;LpnetLong是指標,該指標指向的內容是轉換後的網路位元組序。如果轉換成功,該WSAHtonl()函式返回值是0,否則返回值是SOCKET_ERROR。

在使用Winsock函式進行網路程式設計時,可以使用以上兩個函式。

3.2 網路位元組序轉換成主機位元組序

可以通過ntohl()、ntohs()、WSANtohl()和WSANtohs()函式實現主機位元組序轉換成網路位元組序。使用的方法與“3.1 主機位元組序轉換成網路位元組序”中介紹的相應的四個函式類似。

4 網路地址的賦值

SOCKADDR_IN結構用來表示網路地址和埠。

4.1 SOCKADDR_IN結構

該結構的定義為

typedef struct sockaddr_in

{

short sin_family

; ADDRESS_FAMILY sin_family

; USHORT sin_port

; IN_ADDR sin_addr

; CHAR sin_zero[8];

}SOCKADDR_IN, *PSOCKADDR_IN;

其中,sin_family表示地址族;sin_port是通訊的埠號,該埠號是網路位元組序;sin_addr是IN_ADDR結構的物件,表示IP地址,該IP地址也是網路位元組序;sin_zero是字元陣列,為了使得SOCKADDR_IN結構與SOCKADDR的結構的大小相同,用sin_zero來作為SOCKADDR_IN結構的填充。

4.2 IN_ADDR結構

在“4.1 SOCKADDR_IN結構”中提到了該結構的成員變數sin_addr是IN_ADDR結構的物件。IN_ADDR結構的定義為

typedef struct in_addr {

  union {

    struct {

      UCHAR s_b1;

      UCHAR s_b2;

      UCHAR s_b3;

      UCHAR s_b4;

    } S_un_b;

    struct {

      USHORT s_w1;

      USHORT s_w2;

    } S_un_w;

    ULONG  S_addr;

  } S_un;

} IN_ADDR, *PIN_ADDR, *LPIN_ADDR;

從ADDR_IN結構的定義可以看出,該結構是一個union,包含了IP地址的三種表達方式。其中S_un_b的四個成員變數分別表示點格式IP地址的四個部分;S_un_w的兩個成員變數分別表示IP地址的高字部分和低字部分;S_addr是IP地址的無符號長整形格式。

4.3 程式碼實現

SOCKADDR_IN addr;

addr.sin_family = AF_INET;

inet_pton(AF_INET, "192.168.1.1", &addr.sin_addr.S_un.S_addr);

addr.sin_port = htons(12345);

AF_INET表示使用的IP族;inet_pton()函式的作用是將點格式的IP地址轉換為數字,該函式的第3個引數是SOCKADDR_IN結構的S_addr,即無符號長整形,inet_pton()函式的使用方法請參考《VS2015中IP地址轉換函式》;通過htons()函式將主機位元組序的埠轉換為網路位元組序,並將其儲存在SOCKADDR_IN結構的sin_port中。圖3是addr.sin_port的值。

圖3 addr.sin_port的值

因為12345的主機位元組序是0x3039,所以其網路位元組序是0x3930。

相關連結:使用inet_pton()函式將IP地址格式進行轉換時,還可以使用如下程式碼

inet_pton(AF_INET, "192.168.1.1", &addr.sin_addr.s_addr);

因為在inaddr.h中對s_addr進行了定義

#define s_addr  S_un.S_addr /* can be used for most tcp & ip code */