目錄
前言
說明:
- 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即可。
參考
參考
- 《TCP/IP網路程式設計》
- 李柱明部落格-TCP/IP TCP詳細筆記
- github 阿婆主