ip(點分十進制 <==> 二進制整數)之間的轉換

linux的套接字部分比較容易混亂,在這裏稍微總結一下。linux

地址轉換函數在地址的文本表達式它們存放在套接字地址結構中的二進制值進行轉換。網絡

地址轉換函數有四個:其中inet_addr 和 inet_ntoa適用於IPv4,inet_pton 和 inet_ntop同時適於用IPv4和IPv6。socket

  • 套接字地址結構分爲IPv4套接字地址結構sockaddr_in和IPv6套接字地址結構sockaddr_in6。其中IPv4的套接字地址結構以下。
    • IPv4套接字地址結構:(定義在<netinet/in.h>頭文件中)
       1 struct in_addr{
       2     in_addr_t s_addr;    //
       3 };
       4 
       5 struct sockaddr_in{
       6     uint8_t          sin_len;
       7     sa_family_t      sin_family;    //套接字地址結構的地址族
       8     in_port_t        sin_port       //TCP或UDP端口,通常爲uint16_t
       9     struct in_addr  sin_addr;       //IPv4地址,通常爲uint32_t
      10     char             sin_zero[8];
      11 };
      • 說明:POSIX規範只須要這個結構中的3個字段:sin_family、sin_addr和sin_port。對於符合POSIX的實現來講,定義額外的結構字段是能夠接受的。幾乎全部的實現都增長了sin_zero字段,因此全部的套接字地址結構大小都至少是16字節。函數

      • IPv4地址和TCP或UDP端口號在套接字地址結構中老是以網絡字節序來存儲,在使用這些字段時,必需要牢記這一點。
      • 32位IPv4地址存在兩種不一樣的訪問方法。例如:若是serv第一位某個網際套接字地址結構,那麼serv.sin_addr將按in_addr結構引用其中的32位IPv4地址,而serv.sin_addr.s_addr將按in_addr_t(一般是一個uint32_t)引用同一個32位Ipv4地址。在將它做爲函數的參數時要注意使用正確的IPv4地址,由於編譯器對傳遞結構和傳遞正數的處理是徹底不一樣的。
      • sin_zero字段不曾使用,不過在填寫這種套接字地址結構時,老是把該字段置爲0(老是在填寫前把整個結構置爲0)
      • 套接字地址結構僅在給定主機上使用:雖然結構中的某些字段(例如IP地址和端口號)用在不一樣主機之間的通訊,可是結構自己並不在主機之間傳遞。
  • 通用套接字地址結構:
    • 看成爲一個參數傳遞進任何套接字函數時,套接字地址結構老是以引用形式(也就是以指向該結構的指針)來傳遞。然而以這樣的指針做爲參數之一的任何套接字函數必須處理來自全部支持的任何協議族的套接字地址結構。在如何聲明所傳遞指針的數據類型上存在一個問題,有了ANSI C後解決辦法:void *。然而在ANSI C以前的解決辦法是在<sys/socket.h>中定義一個通用的套接字地址結構:
      1 struct sockaddr{
      2     uint8_t      sa_len;
      3     sa_family_t  sa_family;
      4     char         sa_data[14];
      5 };

      因而套接字函數被定義爲以指向某個通用套接字地址結構的一個指針做爲其參數之一,例如bind函數的ANSI C函數原型:ui

      int bind(int, struct sockaddr *, socklen_t);    //通常是uint32_t

      這就要求對這些函數的任何調用都必需要將指向特定於協議的套接字地址結構的指針進行強制類型轉換,變成指向某個通用套接字地址類型的指針,如:spa

      struct sockaddr_in serv;
      
      bind(sockfd, (struct sockaddr *) &serv, sizeof(serv));
  • 值--結果參數
    • 當向一個套接字函數傳遞一個套接字地址結構時,該結構老是以引用形式來傳遞,也就是說傳遞的是一個指向該結構的一個指針。該結構的長度也做爲一個參數來傳遞,可是其傳遞方式取決於該結構的傳遞方向:進程-->內核,內核-->進程
      • 從進程到內核傳遞套接字地址結構的函數有3個:bind,connect,sendto。這些函數的一個參數是指向某個套接字地址結構的指針,另外一個參數是該結構的整數大小,例如:
        struct sockaddr_in serv;
        
        connect(sockdf, (SA *) &serv, sizof(serv));
      • 從內核到進程傳遞套接字地址結構的函數有4個:accept, recvfrom,getsockname和getpeername。這4個函數的其中兩個參數是指向某個套接字地址結構的指針和指向表示該結構大小的整數的指針,如:
        struct sockaddr_un cli;
        socklen_t len;
        
        len = sizeof(cli);
        getpeername(unixfd, (SA *) &cli, &len);
    • 把套接字地址結構大小這個參數從一個整數改成指向某個整數變量的指針,其緣由在於:當單數被調用時,結構大小是一個值,它告訴內核結構的大小,這樣內核在寫該結構時不至於越界;當函數返回時,結構大小又是一個結果,它告訴進程內核在該結構中究竟存儲了多少信息。這種類型的參數成爲值-結果參數。
  • 網絡字節序 <--> 主機字節序:有四個函數用於網絡字節序和主機字節序之間的轉換:
    • 如下兩個均返回網絡字節序
    • uint16_t htons(uint16_t host16bitvalue);
    • uint32_t htonl(uint32_t host32bitvalue);
    • 如下兩個均返回主機字節序
    • uint16_t ntohs(uint16_t net16bitvalue);
    • uint32_t ntohs(uint32_t net32bitvalue);
  • 地址轉換函數(兩組):
    • inet_aton, inet_addr, inet_ntoa:這一組函數在點分十進制字符串("206.168.112.96")與它長度爲32位的網絡字節序二進制值間轉換IPv4地址。原型以下:
      #include <arpa/inet.h>
      
      int inet_aton(const char *strptr, struct in_addr *addrptr);    //返回:若字符串有效則爲1,不然爲0
      
      in_addr_t inet_addr(const char *strptr);                       //返回:若字符串有效則爲32位二進制網絡字節序的IPv4地址,不然爲INADDR_NONE
      
      char *inet_ntoa(struct in_addr inaddr);              //返回:指向一個點分十進制數串的指針

      //還有一個名爲inet_network的函數和inet_addr相似,可是其返回的是主機字節序
      in_addr_t inet_network(const char *strptr);

      inet_aton 和 inet_addr 兩個函數都是將一個點分十進制字符串轉換成一個32位的網絡字節序二進制值。inet_aton函數,若是addrptr指針爲空,那麼該函數仍然對輸入的字符串執行有效性檢查,可是不存儲任何結果。inet_addr函數存在一些問題:全部2^32個可能的二進制值都是有效的ip地址(0.0.0.0-255.255.255.255),可是當出錯時函數返回INADDR_NONE常量(一般是一個32位均爲1的值),這意味着點分十進制數串255.255.255.255不能由該函數處理,由於它的二進制用來指示該函數失敗,inet_addr還存在一個潛在的問題:一些手冊聲明該函數出錯時返回-1而不是INADDR_NONE,這樣在對該函數的返回值和一個負常量進行比較時可能會發生問題。現在,inet_addr已經廢棄,新的代碼應該改用inet_ato函數,更好的解決方法是使用inet_pton函數。另外須要注意的是inet_ntoa函數的參數是in_addr的結構體而不是指針。unix

    • inet_pton,inet_ntop這一組函數對IPv4和IPv6地址都適用。原型以下:
      #include <arpa/inet.h>
      
      int inet_pton(int family, const char *strptr, void *addrptr);                //返回:若成功則爲1,若輸入不是有效的表達格式則爲0,若出錯則爲-1
      
      const char *inet_ntop(int family, void *addrptr, char *strptr, size_t len);  //返回:若成功則爲指向結果的指針,若出錯則爲NULL
      //len:INET_ADDRSTRLEN INET6_ADDRSTRLEN
      //inet_ntop函數的strptr不能夠是一個空指針,調用者必須爲目標存儲單元分配內存並制定其大小,調用成功時,這個指針就是該函數的返回值
    • 地址轉換函數小結:
                                inet_pton(AF_INET),inet_aton, inet_addr
      點分十進制數     ----------------------------------------------------->          in_addr{}
       IPv4地址       <-----------------------------------------------------       32位二進制IPv4地址
                                inet_ntop(AF_INET),inet_ntoa

       

      #include <stdio.h>
      #include <string.h>
      #include <arpa/inet.h>
      
      int main(int argc, char **argv)
      {
          const char *ip_str = "127.0.0.1";
          char *ip_res;
          in_addr_t addr_t;
          struct in_addr addr;
      
          //1.str -> binary
          inet_aton(ip_str, &addr);
          printf("inet_aton::%x\n", addr);                //1000007f
      
          addr_t = inet_addr(ip_str);
          printf("inet_addr::%x\n", addr_t);              //1000007f
      
          inet_pton(AF_INET, ip_str, (void *)&addr);      //1000007f
          printf("inet_pton::%x\n", addr);
      
          //2.binary -> str
          ip_res = inet_ntoa(addr);
          printf("inet_ntoa::%s\n", ip_res);              //127.0.0.1
      
          inet_ntop(AF_INET, &addr, ip_res, INET_ADDRSTRLEN);
          printf("inet_ntop::%s\n", ip_res);              //127.0.0.1
      
          return 0;
      }
      
      output:
      inet_aton::100007f
      inet_addr::100007f
      inet_pton::100007f
      inet_ntoa::127.0.0.1
      inet_ntop::127.0.0.1
相關文章
相關標籤/搜索