TCP/IP網絡編程之域名及網絡地址


域名系統

DNS是對IP地址和域名進行互相轉換的系統,其核心是DNS服務器。提供網絡服務的服務端也是通過IP地址來區分的,但由於IP地址難於記憶,因此通過容易記憶並表述的域名來取代IP地址

在瀏覽器地址欄輸入www.baidu.com,或如圖1-1用ping命令獲取其IP地址,便可訪問百度主頁,那么通用域名訪問和通過IP訪問這二者有何區別?

圖1-1   用ping命令獲取百度地址

實際上,域名是賦予服務端的虛擬地址,而非實際地址。因此需要將虛擬地址轉化為實際地址。那么,如何將域名轉化為IP地址呢?DNS服務器承擔此重任,可以向DNS服務器請求轉換地址。所有計算機中都記錄着默認DNS服務器地址,就是通過默認DNS服務器得到相應域名的IP地址信息。在瀏覽器地址欄中輸入域名后,瀏覽器通過默認DNS服務器獲取該域名對應的IP地址信息,之后才真正接入該網站

計算機內置的默認DNS服務器並不知道網絡上所有域名的IP地址信息,若該DNS服務器無法解析,則會詢問其他DNS服務器並提供給用戶,如圖1-2

圖1-2   DNS和請求獲取IP地址信息

圖1-2展示了默認DNS服務器無法解析主機詢問的域名對應IP地址時的應答過程,可以看出,默認DNS服務器收到自己無法解析的請求時,向上級DNS服務器詢問,通過這種方式逐級向上傳遞信息,到達頂級DNS服務器時——根DNS服務器,它知道該向哪個DNS服務器詢問,向下級DNS傳遞解析請求,得到IP地址后原路返回,最終將解析的IP地址傳遞到發起請求的主機,DNS就是這樣層次化管理的一種分布式數據庫系統

利用域名獲取IP地址

#include <netdb.h>
struct hostent *gethostbyname(const char *hostname);//成功時返回hostent結構體指針,失敗時返回NULL指針

  

只要向這個函數傳遞域名字符串,就會返回域名所對應的IP地址。只是返回時,地址信息裝入hostent結構體,此結構體定義如下:

struct hostent
{
	char *h_name;         //正式主機名
	char **h_aliases;     //主機別名
	int h_addrtype;       //主機IP地址類型:IPV4-AF_INET
	int h_length;		  //主機IP地址字節長度,對於IPv4是四字節,即32位
	char **h_addr_list;	  //主機的IP地址列表
};

  

從上述結構體可以看出,當調用gethostbyname函數時不止返回IP信息,同時還帶着其他信息,下面簡要介紹下上述結構體中的各個成員:

  • h_name:該變量存有官方域名,官方域名代表某一主頁,但實際上,一些著名公司的域名並未使用官方域名注冊
  • h_aliases:可以通過多個域名訪問同一主頁,同一IP可以綁定多個域名,因此,除官方域名外還可指定其他域名
  • h_addrtype:gethostbyname函數不僅支持IPv4,還支持IPv6。因此可以通過此變量獲取保存在h_addr_list的IP地址的地址族信息。若是IPv4,則此變量存有AF_INET
  • h_length:保存IP地址長度。若是IPv4地址,因為是4個字節,則保存4;若是IPv6,因為是16個字節,故保存16
  • h_addr_list:該變量以整數形式保存域名的IP地址。另外,用於訪問量較大的網站可能分配多個IP給同一域名,利用多個服務器進行負載均衡,同樣可以通過此變量獲取IP地址信息

調用gethostbyname函數后返回的hostent結構體變量結構如圖1-3所示:

圖1-3   hostent結構體變量

下面我們看一下gethostbyname函數的應用,並說明hostent結構體變量的特性

gethostbyname.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netdb.h>
void error_handling(char *message);

int main(int argc, char *argv[])
{
    int i;
    struct hostent *host;
    if (argc != 2)
    {
        printf("Usage:%s<addr>\n", argv[0]);
        exit(1);
    }
    host = gethostbyname(argv[1]);
    if (!host)
        error_handling("gethost... error");
    printf("Official name:%s\n", host->h_name);
    for (i = 0; host->h_aliases[i]; i++)
        printf("Aliases %d:%s\n", i + 1, host->h_aliases[i]);
    printf("Address type:%s\n", (host->h_addrtype == AF_INET) ? "AF_INET" : "AF_INET6");
    for (i = 0; host->h_addr_list[i]; i++)
        printf("IP addr %d:%s\n", i + 1, inet_ntoa(*(struct in_addr *)host->h_addr_list[i]));

    return 0;
}
void error_handling(char *message)
{
    fputs(message, stderr);
    fputc('\n', stderr);
    exit(1);
}

  

  • 第17行:通過main函數傳遞的字符串作為參數傳給gethostbyname函數
  • 第20行:輸出官方域名
  • 第21、22行:輸出除官方域名以外的域名
  • 第24、25行:輸出IP地址信息

編譯gethostbyname.c並運行

# gcc gethostbyname.c -o gethostbyname
# ./gethostbyname www.taobao.com
Official name:www.taobao.com.danuoyi.tbcache.com
Aliases 1:www.taobao.com
Address type:AF_INET
IP addr 1:61.154.126.109
IP addr 2:218.67.61.254

  

大家可在編譯gethostbyname.c后自行選擇一個域名進行測試,現在我們看一下第24和25行,如果我們只看hostent結構體的定義,結構體成員h_addr_list指向了一個字符串指針數組(由多個字符串地址構成的數組)。但字符串指針數組中的元素實際指向的是in_addr結構體地址而非字符串,如圖1-4所示:

圖1-4   h_addr_list結構體成員

圖1-4給出了h_addr_list結構體的參照關系,正因如此,上述代碼中才有了第25行的類型轉換,並調用inet_ntoa函數

這里可能大家會有疑問,為什么是char *而不是in_addr *?hostent結構體的成員h_addr_list指向的數組類型並不是in_addr結構體的指針數組,而是采用了char指針。是因為hostent結構體並非只為IPv4准備,因此考慮到通用性,聲明為char指針類型的數組。那么,聲明為void指針類型的數組是否更合理?確實,如果指針對象不明確時,聲明為void指針類型更合理,但我們所學習的套接字函數是早在void指針標准化之前定義的,在當時無法確定指針類型時都采用char指針

利用IP地址獲取域名

之前介紹的gethostbyname函數利用域名獲取包括IP地址在內的域相關信息,而gethostbyaddr函數利用IP地址獲取域相關信息

#include <sys/socket.h>
struct hostent *gethostbyaddr(const char *addr, socklen_t len, int family);//成功時返回hostent結構體變量地址值,失敗時返回NULL指針

  

  • addr:含有IP地址信息的in_addr結構體指針,為了同時傳遞IPv4地址之外的其他信息,該變量的類型聲明為char指針
  • len:向第一個參數傳遞的地址信息的字節數,IPv4時為4,IPv6時為16
  • family:傳遞地址族信息,IPv4時為AF_INET,IPv6時為AF_INET6

現在我們來看一下gethostbyaddr函數的使用方法

gethostbyaddr.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netdb.h>
void error_handling(char *message);

int main(int argc, char *argv[])
{
    int i;
    struct hostent *host;
    struct sockaddr_in addr;
    if (argc != 2)
    {
        printf("Usage : %s <IP>\n", argv[0]);
        exit(1);
    }

    memset(&addr, 0, sizeof(addr));
    addr.sin_addr.s_addr = inet_addr(argv[1]);
    host = gethostbyaddr((char *)&addr.sin_addr, 4, AF_INET);
    if (!host)
        error_handling("gethost... error");

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

    for (i = 0; host->h_aliases[i]; i++)
        printf("Aliases %d: %s \n", i + 1, host->h_aliases[i]);

    printf("Address type: %s \n",
           (host->h_addrtype == AF_INET) ? "AF_INET" : "AF_INET6");

    for (i = 0; host->h_addr_list[i]; i++)
        printf("IP addr %d: %s \n", i + 1,
               inet_ntoa(*(struct in_addr *)host->h_addr_list[i]));
    return 0;
}

void error_handling(char *message)
{
    fputs(message, stderr);
    fputc('\n', stderr);
    exit(1);
}

  

除第22行的gethostbyaddr函數調用外,與gethostbyname.c並無區別,因為函數調用的結果是通過hostent結構體變量地址值傳遞的

編譯gethostbyaddr.c並運行

# gcc gethostbyaddr.c -o gethostbyaddr
# ./gethostbyaddr 218.67.61.254
Official name: 254.61.67.218.broad.sm.fj.dynamic.163data.com.cn 
Address type: AF_INET 
IP addr 1: 218.67.61.254

  

之前我們通過gethostbyname.c獲得了某寶的IP地址,現在我們嘗試用之前獲得的IP地址反過來獲取域名信息。從運行結果可以看到,記錄於DNS的官方主頁地址具有特殊格式


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM