天天看點

ntohs, ntohl, htons,htonl的比較和詳解

在C/C++寫網絡程式的時候,往往會遇到位元組的網絡順序和主機順序的問題。

這時就可能用到htons(), ntohl(), ntohs(),htons()這4個網絡位元組順序與本地位元組順序之間的轉換函數:

      htonl()--"Host to Network Long int"     32Bytes       ntohl()--"Network to Host Long int"     32Bytes       htons()--"Host to Network Short int"   16Bytes       ntohs()--"Network to Host Short int"   16Bytes

之是以需要這些函數是因為計算機資料表示存在兩種位元組順序:NBO與HBO。

網絡位元組順序NBO(Network Byte Order):

      按從高到低的順序存儲,在網絡上使用統一的網絡位元組順序,可以避免相容性問題。

主機位元組順序(HBO,Host Byte Order):

      不同的機器HBO不相同,與CPU設計有關,資料的順序是由cpu決定的,而與作業系統無關。 

      如 Intel   x86結構下,short型數0x1234表示為34 12, int型數0x12345678表示為78 56 34 12 。

      如IBM   power PC結構下,short型數0x1234表示為12   34, int型數0x12345678表示為12 34 56 78。   

 由于這個原因不同體系結構的機器之間無法通信,是以要轉換成一種約定的數序,也就是網絡位元組順序,其實就是如同power   pc那樣的順序 。在PC開發中有ntohl和htonl函數可以用來進行網絡位元組和主機位元組的轉換。

在Linux系統下:htonl(),htons(), ntohl(), ntohs()的頭檔案及函數定義:

  #include <arpa/inet.h>

  uint32_t htonl(uint32_t hostlong);

  uint16_t htons(uint16_t hostshort);

  uint32_t ntohl(uint32_t netlong);

  uint16_t ntohs(uint16_t netshort);

在windows系統下:htonl(),htons(), ntohl(), ntohs(), inet_addr()使用說明

  簡述:

  将一個無符号短整形數從網絡位元組順序轉換為主機位元組順序。

  #include <winsock.h>

  u_short PASCAL FAR ntohs( u_short netshort);

  netshort:一個以網絡位元組順序表達的16位數。

  注釋:

  本函數将一個16位數由網絡位元組順序轉換為主機位元組順序。

  傳回值:ntohs()傳回一個以主機位元組順序表達的數。

ntohl() 

       簡述:

  将一個無符号長整形數從網絡位元組順序轉換為主機位元組順序。

  #include <winsock.h>

  u_long PASCAL FAR ntohl( u_long netlong);

  netlong:一個以網絡位元組順序表達的32位數。

  本函數将一個32位數由網絡位元組順序轉換為主機位元組順序。

  傳回值:

  ntohl()傳回一個以主機位元組順序表達的數。

htons()

  将主機的無符号短整形數轉換成網絡位元組順序。//将無符号短整型主機位元組序轉換為網絡位元組序

  u_short PASCAL FAR htons( u_short hostshort);

  hostshort:主機位元組順序表達的16位數。

  本函數将一個16位數從主機位元組順序轉換成網絡位元組順序。

  htons()傳回一個網絡位元組順序的值。

  簡單地說,htons()就是将一個數的高低位互換

  (如:12 34 --> 34 12)

  VB表示:

  MsgBox Hex(htons(&H1234))

  顯示值為 3412

htonl()

      簡述:

  将主機的無符号長整形數轉換成網絡位元組順序。//将無符号長整型網絡位元組序轉換為主機位元組序

  u_long PASCAL FAR htonl( u_long hostlong);

  hostlong:主機位元組順序表達的32位數。

  本函數将一個32位數從主機位元組順序轉換成網絡位元組順序。

  htonl()傳回一個網絡位元組順序的值。

inet_addr()

       将一個點間隔位址轉換成一個in_addr。

  unsigned long PASCAL FAR inet_addr( const struct FAR* cp);

  cp:一個以Internet标準“.”間隔的字元串。例如202.38.214.xx

       當IP位址為255.255.255.255是被認為無效IP位址。

       本函數解釋cp參數中的字元串,這個字元串用Internet的“.”間隔格式表示一個數字的Internet位址。

    傳回值:

    一個無符号長整形數,可用作Internet位址。所有Internet位址以網絡位元組順序傳回(位元組從左到右排列)。

inet_ntoa()

    簡述:

  将網絡位址轉換成“.”點隔的字元串格式。

  char FAR* PASCAL FAR inet_ntoa( struct in_addr in);

  in:一個表示Internet主機位址的結構。

本函數将一個用in參數所表示的Internet位址結構轉換成以“.” 間隔的諸如“a.b.c.d”的字元串形式。請注意inet_ntoa()傳回的字元串存放在WINDOWS套接口實作所配置設定的記憶體中。應用程式不應假設該記憶體是如何配置設定的。在同一個線程的下一個WINDOWS套接口調用前,資料将保證是有效。

當IP位址為255.255.255.255是認為有效IP位址。這是與inet_addr()的差別

  若無錯誤發生,inet_ntoa()傳回一個字元指針。否則的話,傳回NULL。其中的資料應在下一個WINDOWS套接口調用前複制出來。

inet_aton()

    與inet_ntoa()作用相反。

inet_pton()

    本函數将點分十進制轉換為整數

      #include <sys/types.h>

  #include <sys/socket.h>

  int inet_pton(int af, const char *src, void *dst);

  這個函數轉換字元串到網絡位址,第一個參數af是位址族,轉換後存在dst中

    inet_pton 是inet_addr的擴充,支援的多位址族有下列:

  af = AF_INET

  src為指向字元型的位址,即ASCII的位址的首位址(ddd.ddd.ddd.ddd格式的),函數将該位址

  轉換為in_addr的結構體,并複制在*dst中

  af =AF_INET6

  src為指向IPV6的位址,,函數将該位址轉換為in6_addr的結構體,并複制在*dst中

  如果函數出錯将傳回一個負值,并将errno設定為EAFNOSUPPORT,如果參數af指定的位址族和src格式不對,函數将傳回0。

       #include <sys/types.h>

  const char *inet_ntop(int af, const void *src, char *dst, socklen_t cnt);

  這個函數轉換網絡二進制結構到ASCII類型的位址,參數的作用和上面相同,隻是多了一個參數socklen_t cnt,他是所

    指向緩存區dst的大小,避免溢出,如果緩存區太小無法存儲位址的值,則傳回一個空指針,并将errno置為ENOSPC

atoi()

array to integer将字元串轉換為整形數

首先,假設你已經有了一個sockaddr_in結構體ina,你有一個IP位址"132.241.5.10" 要儲存在其中,你就要用到函數inet_addr(),将IP位址從 點數格式轉換成無符号長整型。

使用方法如下:

ina.sin_addr.s_addr = inet_addr("132.241.5.10");

注意,inet_addr()傳回的位址已經是網絡位元組格式,是以你無需再調用 函數htonl()。

我們現在發現上面的代碼片斷不是十分完整的,因為它沒有錯誤檢查。 顯而易見,當inet_addr()發生錯誤時傳回-1。記住這些二進制數字?(無符 号數)-1僅僅和IP位址255.255.255.255相符合!這可是廣播位址!大錯特 錯!記住要先進行錯誤檢查。

好了,現在你可以将IP位址轉換成長整型了。有沒有其相反的方法呢? 它可以将一個in_addr結構體輸出成點數格式?這樣的話,你就要用到函數 inet_ntoa()("ntoa"的含義是"network to ascii"),就像這樣: 

printf("%s",inet_ntoa(ina.sin_addr));

它将輸出IP位址。需要注意的是inet_ntoa()将結構體in-addr作為一 個參數,不是長整形。同樣需要注意的是它傳回的是一個指向一個字元的 指針。它是一個由inet_ntoa()控制的靜态的固定的指針,是以每次調用 inet_ntoa(),它就将覆寫上次調用時所得的IP位址。例如:

char *a1, *a2;

a1 = inet_ntoa(ina1.sin_addr); /* 這是198.92.129.1 */

a2 = inet_ntoa(ina2.sin_addr); /* 這是132.241.5.10 */

printf("address 1: %s ",a1);

printf("address 2: %s ",a2);

輸出如下:

address 1: 132.241.5.10

address 2: 132.241.5.10

假如你需要儲存這個IP位址,使用strcopy()函數來指向你自己的字元指針。

***********************************************************************************************************************************

測試代碼如下

#include 

int main(int argc, char* argv[])

{

         struct in_addr addr1,addr2;

         ulong   l1,l2;

         l1= inet_addr("192.168.0.74");

         l2 = inet_addr("211.100.21.179");

         memcpy(&addr1, &l1, 4);

         memcpy(&addr2, &l2, 4);

         printf("%s : %s ", inet_ntoa(addr1), inet_ntoa(addr2));    //注意這一句的運作結果

         printf("%s ", inet_ntoa(addr1));

         printf("%s ", inet_ntoa(addr2));

         return 0;

}

實際運作結果如下:

192.168.0.74 : 192.168.0.74       //從這裡可以看出,printf裡的inet_ntoa隻運作了一次。

192.168.0.74

211.100.21.179

inet_ntoa傳回一個char *,而這個char *的空間是在inet_ntoa裡面靜态配置設定的,是以inet_ntoa後面的調用會覆寫上一次的調用。第一句printf的結果隻能說明在printf裡面的可變參數的求值是從右到左的,僅此而已。

繼續閱讀