【網絡編程】TCPIP-7-域名與網絡地址



前言

說明:

  • demo 基於 Linux。

7. 域名與網絡地址

本節主要記錄三個名詞:DNS、域名及IP。

7.1 IP

IP地址:網絡層的主機地址。老生常談啦
目前分為IPV4和IPV6。

如百度的地址之一:202.108.22.5

7.2 域名

域名就是IP的別名吧,因為人類對數字的記憶較難,所以把某個IP起個別名。

如百度的域名:www.baidu.com
在瀏覽器地址欄中輸入其域名就可以訪問到百度的服務器,不用記其IP地址了。

7.3 DNS

DNS 是對IP地址和域名進行相互轉換的系統,其核心是 DNS 服務器。

因為 TCP/IP 協議棧中網絡層需要的是 IP 地址,而不是域名,所以得需要把域名轉換成對於的IP地址。

如圖:

7.4 IP地址與域名之間的轉換

7.4.1 利用域名獲取IP地址

struct hostent
{
    char *h_name;       /* Official name of host.  */
    char **h_aliases;   /* Alias list.  */
    int h_addrtype;     /* Host address type.  */
    int h_length;       /* Length of address.  */
    char **h_addr_list; /* List of addresses from name server.  */
};

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

struct hostent

  • h_name:該變量中存有官方域名(Official domain name)。官方域名代表某一主頁,但實際上,一些著名公司的域名並沒有用官方域名注冊。
  • h_aliases:可以通過多個域名訪問同一主頁。同一IP可以綁定多個域名,因此,除官方域名外還可以指定其他域名。這些信息可以通過 h_aliases 獲得。
  • 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地址信息。

7.4.2 利用IP地址獲取域名

/*
	addr: 含有IP地址信息的 in_addr 結構體指針。為了同時傳遞 IPV4 地址之外的全部信息,該變量的類型聲明為 char 指針
	len: 向第一個參數傳遞的地址信息的字節數,IPV4時為 4 ,IPV6 時為16.
	family: 傳遞地址族信息,ipv4 是 AF_INET ,IPV6是 AF_INET6
	成功時返回 hostent 結構體變量地址值,失敗時返回 NULL 指針
*/
#include <netdb.h>
struct hostent *gethostbyaddr(const char *addr, socklen_t len, int family);

7.4.3 升級版的API

getaddrinfo() 能夠處理 域名到IP服務到端口 這兩種轉換。可重入的。

getnameinfo() 能夠處理 IP到域名端口到服務 這兩種轉換。可重入的。

#include <sys/socket.h>
#include <netdb.h>

int getaddrinfo(const char *restrict nodename, 	/* host 或者IP地址 */
    const char *restrict servname, 				/* 十進制端口號 或者常用服務名稱如"ftp"、"http"等 */
    const struct addrinfo *restrict hints, 		/* 獲取信息要求設置 */
    struct addrinfo **restrict res); 			/* 獲取信息結果 */

void freeaddrinfo(struct addrinfo *ai); 

// 需要了解的結構體
struct addrinfo {
    int ai_flags;              /* 附加選項,多個選項可以使用或操作結合 */
    int ai_family;             /* 指定返回地址的協議簇,取值范圍:AF_INET(IPv4)、AF_INET6(IPv6)、AF_UNSPEC(IPv4 and IPv6) */ 
    int ai_socktype;           /* enum __socket_type 類型,設置為0表示任意類型 */
    int ai_protocol;           /* 協議類型,設置為0表示任意類型 */
    socklen_t ai_addrlen;      /* socket address 的長度 */
    struct sockaddr *ai_addr;  /* socket address 的地址 */
    char *ai_canonname;        /* Canonical name of service location. */
    struct addrinfo *ai_next;  /* 指向下一條信息,因為可能返回多個地址 */
};

// The sockaddr structure is used to define a socket address which is used in the bind(), connect(), getpeername(), getsockname(), recvfrom(), and sendto() functions.
struct sockaddr
{
    sa_family_t  sa_family;   //Address family. 
    char         sa_data[14]; // Socket address (variable-length data). 
};
// 該結構體一般都會轉為
struct sockaddr_in
{
    sa_family_t sin_family;  //地址族(Address Family)
    uint16_t sin_port;       //16 位 TCP/UDP 端口號
    struct in_addr sin_addr; //32位 IP 地址
    char sin_zero[8];        //為了保證其大小一致,目前不使用
};
struct in_addr
{
    in_addr_t s_addr; //32位IPV4地址
}

getaddrinfo() 參數說明:

  • nodename:主機名:
    • 域名:"www.baidu.com"
    • 數字化的地址字符串IPv4的點分十進制串:"192.168.1.100"
    • 數字化的地址字符串IPv6的16進制串:"2000::1:2345:6789:abcd"
    • 注意:如果 ai_flags 中設置了 AI_NUMERICHOST 標志,那么該參數只能是數字化的地址字符串,不能是域名。該標志的作用就是阻止進行域名解析。
  • servname:服務名。
    • 可以是十進制的端口號("8080")字符串,也可以是已定義的服務名稱,如"ftp"、"http"等。
    • 如果為NULL,那么返回的socket地址中的端口號不會被設置。
    • 注意:如果 ai_flags 設置了 AI_NUMERICSERV 標志並且該參數未設置為NULL,那么該參數必須是一個指向10進制的端口號字符串,不能設定成服務名,該標志就是用來阻止服務名解析。
  • hints:該參數指向用戶設定的 struct addrinfo 結構體。
    • 只能設定該結構體中 ai_family、ai_socktype、ai_protocol 和 ai_flags 四個域。
    • 其他域必須設置為0 或者 NULL。
    • 結構體參數參考上面代碼注釋。
  • res:該參數獲取一個指向存儲結果的 struct addrinfo 結構體列表,使用完成后調用 freeaddrinfo() 釋放存儲結果空間。
  • 返回:
    • 0:成功。
    • 其它錯誤標志:失敗。

getnameinfo() 能夠處理 IP到域名端口到服務 這兩種轉換。可重入的。

#include <netdb.h>
/**
 * @param addr,輸入參數。struct addrinfo中的struct sockaddr
 * @param addrlen,輸入參數。struct addrinfo中的socklen_t 
 * @param host,輸出參數。IP地址
 * @param hostlen,輸入參數。告訴函數host的大小
 * @param serv,輸出參數。端口號
 * @param servlen,輸入參數。告訴函數servlen的大小
 * @param flags,輸入參數。告訴函數struct sockaddr的處理方式
 * 
 * @return 返回零則成功。
 */
int getnameinfo(const struct sockaddr *sockaddr,
                socklen_t addrlen,
                char *host,
                size_t hostlen,
                char *serv,
                size_t servlen,
                int flags);

7.5 小知識

DNS主要基於UDP。

目前全球只有13台域名根服務器。

並不是每條DNS查詢都要到達根服務器的,查詢是逐級查詢,各級都有緩存表,先查緩存表中的,沒有再往上報。

8.8.8.8是Google公司提供的免費DNS服務器的IP地址。

也可以指定本地的DNS服務器,只要能解析域名與IP即可。

參考

參考


免責聲明!

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



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