文章出處

  今天我們說說“Pre-網絡編程”。內容比較雜,但都是在做網絡應用程序開發過程中經常要遇到的問題。 

  一、大端、小端和網絡字節序

  小端字節序:little-endian,將低字節存放在內存的起始地址;

  大端字節序:big-endian,將高字節存放在內存的其實地址。

      例如,數字index=0x11223344,在大小端字節序方式下其存儲形式為:

  上圖一目了然的可以看出大小端字節序的區別。

  還有另外一個概念就是網絡字節序。網絡字節順序是TCP/IP中規定好的一種數據表示格式,它與具體的CPU類型、操作系統等無關,從而可以保證數據在不同主機之間傳輸時能夠被正確解釋。網絡字節順序采用big endian方式。注意:X86系列CPU都是小端little-endian字節序,即低字節存低位,高字節存高位。

  為此,Linux專門提供了字節轉換函數.

unsigned long  int htonl(unsigned long  int hostlong)

unsigned short int htons(unisgned short int hostshort)

unsigned long  int ntohl(unsigned long  int netlong)

unsigned short int ntohs(unsigned short int netshort)

       看個例子:

  在這四個轉換函數中,h代表host,n代表 network,s代表short,l代表long 。htonl()函數的意義是將本機器上的long數據轉化為網絡上的long。其他幾個函數的意義也差不多。

        也就是說對于從網絡上接收到的非單子節的基本數據類型數據,首先需要用ntohl(s)將其轉換成本地字節序;同理,發往網絡的非單子節的基本數據類型數據,首先用htonl(s)將其轉換成網絡字節序。這里最常見的就是IP地址和端口號。

  二、點分十進制格式的IP地址和32bit的IP地址

         我們常見的IP地址都是以點分十進制格式表示,例如“172.18.1.231”。而在程序中基本是以如下的結構表示一個IP:

struct in_addr {
         __be32     s_addr; //其實就是一個32bit的數字

};

       它和點分十進制格式的IP地址可以通過一組API實現相互轉換:

int inet_aton(const char *cp,struct in_addr *inp) 無效的地址cp則返回0;否則返回非0

char *inet_ntoa(struct in_addr in) 將一個32位的IP地址轉換成點分十進制字符串。

       這兩個函數所要求的struct in_addr{}參數均為網絡字節序。

       繼續看例子:
 
 

        “192.168.11.23”轉換成數字就是0xc0a80b17,是網絡字節序的。如果直接打印,那么本地按照小端字節序來輸出,結果為net addr = 170ba8c0,剛好和實際相反。當我們先將其轉換成本地字節序,然后再輸出時結果就OK了,即host addr = c0a80b17。同理,inet_ntoa()也類似。        

  三、網絡主機名和IP地址的對應關系

         在做網絡編程時經常要碰到的一個問題就是根據對方主機名來獲取其IP地址,或者根據IP地址反過來解析主機名和其他信息。Linux提供了兩個常用的API:

struct hostent *gethostbyname(const char *name);

struct hostent *gethostbyaddr(const void *addr, int len, int type);

       這兩個函數失敗時返回NULL且設置h_errno錯誤變量,調用hstrerror(h_errno)或者herror("Error");可以得到詳細的出錯信息。成功時均返回如下結構:       

struct hostent {

     char    *h_name;        /* 主機名*/

     char    **h_aliases;    /* 主機別名的列表*/

     int     h_addrtype;     /* 地址類型,AF_INET或其他*/

     int     h_length;       /* 所占的字節數,IPv4地址都是4字節 */

     char    **h_addr_list;  /* IP地址列表,網絡字節序*/

}

#define h_addr  h_addr_list[0]  /*后向兼容 */

       gethostbyname可以將機器名(如www.google.com)轉換為一個結構指針,gethostbyaddr可以將一個32位的IP地址(C0A80001)轉換為結構指針。對于gethostbyaddr函數來說,輸入參數“addr”的類型根據參數“type”來確定,目前type僅取AF_INET或AF_INET6。例如,type=2(即AF_INET),則addr就必須為struct in_addr{}類型。

       繼續看例子:

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <netdb.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <arpa/inet.h>

int main(int arg,char** argv){

  struct hostent *host,*host2;

  if(NULL == (host = gethostbyname(argv[1]))){

          herror("Error");

          return 1;

  }

 

  printf("name = %s\n",host->h_name);

  printf("aliases = %s\n",*host->h_aliases);

  printf("add type = %d\n",host->h_addrtype);

  printf("len = %d\n",host->h_length);

  printf("IP=%s\n",inet_ntoa(*(struct in_addr*)host->h_addr));

 

  printf("=================================\n");

  struct in_addr maddr;

  if(0 == inet_aton(argv[2],&maddr)){

          return 0;

  }

 

  char* c = (char*)&maddr;

  printf("org = %x.%x.%x.%x\n",*(c)&0xff,*(c+1)&0xff,*(c+2)&0xff,*(c+3)&0xff);

 

  if(NULL == (host2 = gethostbyaddr(&maddr,4,2))){

          printf("Error:%s\n",hstrerror(h_errno));

          return 1;

  }

  printf("name = %s\n",host2->h_name);

  printf("aliases = %s\n",*host2->h_aliases);

  printf("add type = %d\n",host2->h_addrtype);

  printf("len = %d\n",host2->h_length);

  printf("IP=%s\n",inet_ntoa(*(struct in_addr*)host2->h_addr));

 

  return 0;

}

 

         運行結果如下:
       當我們調用gethostbyaddr根據主頁的IP地址獲取其站點信息時返回的錯誤是“未知的主機”錯誤,原因留給大家自己思考吧。這充分說明對了于gethostbyname()函數和gethostbyaddr()函數的調用一定要判斷其返回值。

文章列表


不含病毒。www.avast.com
arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()