VS2010 C 學(xué)習(xí)(4):WinSock域名查詢(xún)解析程序
VS2010 C 學(xué)習(xí)(4):WinSock 域名查詢(xún)解析學(xué)習(xí)VC 編制的Socket 域名查詢(xún)、解析程序,主要練習(xí)網(wǎng)絡(luò)Winsock 的應(yīng)用。一、 主要內(nèi)容:1. 根據(jù)IP 地址查詢(xún)主機(jī)信息;
VS2010 C 學(xué)習(xí)(4):WinSock 域名查詢(xún)解析
學(xué)習(xí)VC 編制的Socket 域名查詢(xún)、解析程序,主要練習(xí)網(wǎng)絡(luò)Winsock 的應(yīng)用。
一、 主要內(nèi)容:
1. 根據(jù)IP 地址查詢(xún)主機(jī)信息;
2. 根據(jù)網(wǎng)址查詢(xún)主機(jī)信息、DNS 解析地址;
二、 設(shè)計(jì)實(shí)現(xiàn):
1. 根據(jù)IP 地址查詢(xún)主機(jī)信息;
根據(jù)IP 地址調(diào)用gethostbyaddr 函數(shù) ,分析hostent 結(jié)構(gòu)體 獲取主機(jī)信息。
2. 根據(jù)網(wǎng)址查詢(xún)主機(jī)信息、DNS 解析地址;
根據(jù)網(wǎng)址 調(diào)用getaddinfo 函數(shù)查詢(xún)DNS 服務(wù)器 ,查詢(xún)鏈表依次獲取該網(wǎng)址的主機(jī)信息,分析SOCKADDR_IN結(jié)構(gòu)體,解析出IP 地址。
三、 基礎(chǔ)知識(shí):
(一) 域名解析DNSR(domain name system resolution)
1. 域名解析
域名解析是把域名指向網(wǎng)站空間IP ,讓人們通過(guò)注冊(cè)的域名可以方便地訪問(wèn)到網(wǎng)站一種服務(wù)。域名解析也叫域名指向、服務(wù)器設(shè)置、域名配置以及反向IP 登記等等。說(shuō)得簡(jiǎn)單點(diǎn)就是將好記的域名解析成IP ,服務(wù)由DNS 服務(wù)器完成,是把域名解析到一個(gè)IP 地址,然后在此IP 地址的主機(jī)上將一個(gè)子目錄與域名綁定。
IP 地址是網(wǎng)路上標(biāo)識(shí)您站點(diǎn)的數(shù)字地址,為了方便記憶,采用域名來(lái)代替IP 地址標(biāo)識(shí)站點(diǎn)地址。域名解析就是域名到IP 地址的轉(zhuǎn)換過(guò)程。域名的解析工作由DNS 服務(wù)器完成。
我們知道域名是為了方便記憶而專(zhuān)門(mén)建立的一套地址轉(zhuǎn)換系統(tǒng),要訪問(wèn)一臺(tái)互聯(lián)網(wǎng)上的服務(wù)器,最終還必須通過(guò)IP 地址來(lái)實(shí)現(xiàn),域名解析就是將域名重新轉(zhuǎn)換為IP 地址的過(guò)程。一個(gè)域名對(duì)應(yīng)一個(gè)IP 地址,一個(gè)IP 地址可以對(duì)應(yīng)多個(gè)域名;所以多個(gè)域名可以同時(shí)被解析到一個(gè)IP 地址。域名解析需要由專(zhuān)門(mén)的域名解析服務(wù)器(DNS ) 來(lái)完成。
解析過(guò)程,比如,一個(gè)域名為:***.com,是想看到這個(gè)現(xiàn)HTTP 服務(wù),如果要訪問(wèn)網(wǎng)站,就要進(jìn)行解析,首先在域名注冊(cè)商那里通過(guò)專(zhuān)門(mén)的DNS 服務(wù)器解析到一個(gè)WEB 服務(wù)器的一個(gè)固定IP 上:211.214.1.***,然后,通過(guò)WEB 服務(wù)器來(lái)接收這個(gè)域名,把***.com這個(gè)域名映射到這臺(tái)服務(wù)器上。那么,輸入***.com這個(gè)域名就可以實(shí)現(xiàn)訪問(wèn)網(wǎng)站內(nèi)容了. 即實(shí)現(xiàn)了域名解析的全過(guò)程;
人們習(xí)慣記憶域名,但機(jī)器間互相只認(rèn)IP 地址,域名與IP 地址之間是對(duì)應(yīng)的,它們之間的轉(zhuǎn)換工作稱(chēng)為域名解析,域名解析需要由專(zhuān)門(mén)的域名解析服務(wù)器來(lái)完成,整個(gè)過(guò)程是自動(dòng)進(jìn)行的。
,域名解析協(xié)議(DNS )用來(lái)把便于人們記憶的主機(jī)域名和電子郵件地址映射為計(jì)算機(jī)易于識(shí)別的IP 地址。DNS 是一種c/s的結(jié)構(gòu),客戶(hù)機(jī)就是用戶(hù)用于查找一個(gè)名字對(duì)應(yīng)的地址,而服務(wù)器通常用于為別人提供查詢(xún)服務(wù)。
2. TTL 值
全稱(chēng)是“生存時(shí)間(Time To Live)”,簡(jiǎn)單的說(shuō)它表示DNS 記錄在DNS 服務(wù)器上緩存時(shí)間。
3. A 記錄
WEB 服務(wù)器的IP 指向A (Address) 記錄是用來(lái)指定主機(jī)名(或域名)對(duì)應(yīng)的IP 地址記錄。
(二) Socket
1. socket 定義
socket 接口是TCP/IP網(wǎng)絡(luò)的API ,socket 接口定義了許多函數(shù)或例程,程序員可以用它們來(lái)開(kāi)發(fā)TCP/IP網(wǎng)絡(luò)上的應(yīng)用程序。
每一個(gè)socket 都用一個(gè)半相關(guān)描述{協(xié)議、本地地址、本地端口}來(lái)表示;一個(gè)完整的套接字則用一個(gè)相關(guān)描述{協(xié)議、本地地址、本地端口、遠(yuǎn) 程地址、遠(yuǎn)程端口}來(lái)表示。
socket 也有一個(gè)類(lèi)似于打開(kāi)文件的函數(shù)調(diào)用,該函數(shù)返回一個(gè)整型的socket 描述符,隨后的連接建立、數(shù)據(jù)傳輸?shù)炔僮鞫?是通過(guò)socket 來(lái)實(shí)現(xiàn)的。
2. Socket 類(lèi)型
● 流式socket (SOCK_STREAM)
流式套接字提供可靠的、面向連接的通信流;它使用TCP 協(xié)議,從而保證了數(shù)據(jù)傳輸?shù)恼_性和順序性。
● 數(shù)據(jù)報(bào)socket (SOCK_DGRAM)
數(shù)據(jù)報(bào)套接字定義了一種無(wú)連接的服務(wù),數(shù)據(jù)通過(guò)相互獨(dú)立的報(bào)文進(jìn)行傳輸,是無(wú)序的,并且不保證是可靠、無(wú)差錯(cuò)的。它使用數(shù)據(jù)報(bào)協(xié)議UDP 。
● 原始socket
原始套接字允許對(duì)底層協(xié)議如IP 或ICMP 進(jìn)行直接訪問(wèn),它功能強(qiáng)大但使用較為不
,便,主要用于一些協(xié)議的開(kāi)發(fā)。
3. 地址數(shù)據(jù)結(jié)構(gòu)
● 使用C/C 開(kāi)發(fā)socket 程序時(shí),使用sockaddr 和sockaddr_in這兩個(gè)結(jié)構(gòu)類(lèi)型來(lái)保存socket 信息。
● 這兩個(gè)數(shù)據(jù)類(lèi)型是等效的,可以相互轉(zhuǎn)化,通常sockaddr_in數(shù)據(jù)類(lèi)型使用更為方便。
struct sockaddr
{
unsigned short sa_family; /*協(xié)議族*/
char sa_data[14]; /*14字節(jié)的 協(xié)議地址,包含該socket 的IP 地址和端口號(hào)。*/ };
struct sockaddr_in
{
short int sa_family; /*協(xié)議族*/
unsigned short int sin_port; /*端口號(hào)*/
struct in_addr sin_addr; /*IP地址*/
unsigned char sin_zero[8]; /*填充0 以保持與struct sockaddr同樣大小*/ };
4. 協(xié)議族
● 上述結(jié)構(gòu)中的sa_family字段用于描述socket 中的協(xié)議族,其定義于netinet/in.h
5. 數(shù)據(jù)存儲(chǔ)優(yōu)先順序
● 計(jì)算機(jī)數(shù)據(jù)存儲(chǔ)有兩種字節(jié)優(yōu)先順序:高位字節(jié)優(yōu)先(稱(chēng)為大端模式)和低位字節(jié)優(yōu)先(稱(chēng)為小端模式,PC 機(jī)通常采用)。
,● Internet 上數(shù)據(jù)以高位字節(jié)優(yōu)先順序在網(wǎng)絡(luò)上傳輸,因此在有些情況下,需要對(duì)這兩個(gè)字節(jié)存儲(chǔ)優(yōu)先順序進(jìn)行相互轉(zhuǎn)化。
● 對(duì)字節(jié)存儲(chǔ)優(yōu)先順序轉(zhuǎn)化可能用到4個(gè)函數(shù):htons()、 ntohs()、htonl()和ntohl()。這4個(gè)函數(shù)分別實(shí)現(xiàn)網(wǎng)絡(luò)字節(jié)序和主機(jī)字節(jié)序的轉(zhuǎn)化,其中h 表示host ,n 表示network ,s 表示short ,l 表示long 。通常16位的IP 端口號(hào)用s ,而IP 地址用l 。 ● 調(diào)用該函數(shù)只是使其得到相應(yīng)的字節(jié)序, 用戶(hù)不需清楚該系統(tǒng)的主機(jī)字節(jié)序和網(wǎng)絡(luò)字節(jié)序是否真正相等。如果是相同不需要轉(zhuǎn)換的話(huà),該系統(tǒng)的這些函數(shù)會(huì)定義成空宏。
6. 字節(jié)優(yōu)先順序轉(zhuǎn)換函數(shù)
7. 地址格式轉(zhuǎn)化
● 通常用戶(hù)在表達(dá)地址時(shí)采用的是點(diǎn)分十進(jìn)制表示的數(shù)值(或者是以冒號(hào)分開(kāi)的十進(jìn)制IPv6地址),而在通常使用的socket 編程中所使用的則是 二進(jìn)制值,這就需要將這兩個(gè)數(shù)值進(jìn)行轉(zhuǎn)換。
● 在IPv4中用到的函數(shù)有inet_aton()、inet_addr()和inet_ntoa(),而 IPv4和IPv6兼容的函數(shù)有inet_pton()和inet_ntop()。由于IPv6是下一代互聯(lián)網(wǎng)的標(biāo)準(zhǔn)協(xié)議,后面涉及的函數(shù)都能夠同時(shí)兼容IPv4和IPv6,但在具體舉例時(shí)仍以IPv4為例。
● 這里inet_pton()函數(shù)是將點(diǎn)分十進(jìn)制地址映射為二進(jìn)制地址,而inet_ntop()是
,將二進(jìn)制地址映射為點(diǎn)分十進(jìn)制地址。
8. inet_pton函數(shù)
9. inet_ntop函數(shù)
10. 主機(jī)名
通常,人們?cè)谑褂眠^(guò)程中都不愿意記憶冗長(zhǎng)的IP 地址,尤其到IPv6時(shí),地址長(zhǎng)度多達(dá)128位。因此,使用主機(jī)名將會(huì)是很好的選擇。
,● 在Linux 中,同樣有一些函數(shù)可以實(shí)現(xiàn)主機(jī)名和地址的轉(zhuǎn)化,最為常見(jiàn)的有g(shù)ethostbyname()、gethostbyaddr()和getaddrinfo()等,它們都可以實(shí)現(xiàn)IPv4和IPv6的地址和主機(jī)名之間的轉(zhuǎn)化。其中g(shù)ethostbyname()將主機(jī)名轉(zhuǎn)化為IP 地址,gethostbyaddr()則是逆操作,將IP 地址轉(zhuǎn)化為主機(jī)名,另外getaddrinfo()還能實(shí)現(xiàn)自動(dòng)識(shí)別IPv4地址和IPv6地址。
● gethostbyname()和gethostbyaddr()都涉及一個(gè)hostent 的結(jié)構(gòu)體。
11. hostent 結(jié)構(gòu)體
struct hostent
{
char *h_name; /*正式主機(jī)名*/
char **h_aliases; /*主機(jī)別名*/
int h_addrtype; /*地址類(lèi)型*/
int h_length; /*地址字節(jié)長(zhǎng)度*/
char **h_addr_list; /*指向IPv4或IPv6的地址指 針數(shù)組*/
};
● 調(diào)用gethostbyname()函數(shù)或gethostbyaddr()函數(shù)后就能返回hostent 結(jié)構(gòu)體的相關(guān)信息。
12. gethostbyname 函數(shù)
? 調(diào)用該函數(shù)時(shí)可以首先對(duì)hostent 結(jié)構(gòu)體中的h_addrtype和h_length進(jìn)行設(shè)置,若為IPv4可設(shè)置為AF_INET和4;若為IPv6可設(shè)置為AF_INET6和16;若不設(shè)置則默認(rèn)為IPv4地址類(lèi)型。
13. getaddrinfo 函數(shù)
,14. addrinfo 結(jié)構(gòu)體
getaddrinfo()函數(shù)涉及一個(gè)addrinfo 的結(jié)構(gòu)體:
struct addrinfo
{
int ai_flags;
/*AI_PASSIVE, AI_CANONNAME;*/ int ai_family; /*地址族*/
int ai_socktype; /*socket類(lèi)型*/
int ai_protocol; /*協(xié)議類(lèi)型*/
size_t ai_addrlen; /*地址字節(jié)長(zhǎng)度*/
char *ai_canonname; /*主機(jī)名*/
struct sockaddr *ai_addr; /*socket結(jié)構(gòu)體*/
struct addrinfo *ai_next; /*下一個(gè)指針鏈表*/
};
15. addrinfo 常見(jiàn)選項(xiàng)值
? 通常服務(wù)器端在調(diào)用getaddrinfo()之前,ai_flags設(shè)置AI_PASSIVE,用于 bind()函數(shù)(用于端口和地址的綁定,后面會(huì)講到),主機(jī)名nodename 通常會(huì)設(shè)置為NULL 。
? 客戶(hù)端調(diào)用getaddrinfo()時(shí),ai_flags一般不設(shè)置AI_PASSIVE,但是主機(jī)名 nodename 和服務(wù)名servname (端口)則應(yīng)該不為空。
? 即使不設(shè)置ai_flags為AI_PASSIVE,取出的地址也可以被綁定,很多程序中ai_flags直接設(shè)置為0,即3個(gè)標(biāo)志位都不設(shè)置,這種情況 下只要hostname 和servname 設(shè)置的沒(méi)有問(wèn)題就可以正確綁定。
/*#include files… */
int main()
{
struct addrinfo hints, *res = NULL;
int rc;
memset(&hints, 0, sizeof(hints));
/*設(shè)置addrinfo 結(jié)構(gòu)體中各參數(shù) */
hints.ai_flags = AI_CANONNAME;
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_DGRAM;
hints.ai_protocol = IPPROTO_UDP;
/*調(diào)用getaddinfo 函數(shù)*/
rc = getaddrinfo("localhost", NULL, &hints, &res);
if (rc != 0)
{
perror("getaddrinfo");
exit(1);
}
else
{
printf("Host name is sn", res->ai_canonname);
}
exit(0);
}
(三) 編程注意
● WS2_32.lib是網(wǎng)絡(luò)套接字的庫(kù)。兩種方法加入
1、 菜單的 項(xiàng)目 - 屬性 - Linker - Input - Additional Dependencies 加上
2、 直接在代碼里面 #pragma comment(lib, "ws2_32.lib")
● WSAStartup
使用Winsock 庫(kù)函數(shù)之前, 必須先調(diào)用函數(shù)WSAStartup, 該函數(shù)負(fù)責(zé)初始化動(dòng)態(tài)連接庫(kù)Ws2_32.dll.
函數(shù)定義:
int WSAStartup ( WORD wVersionRequested, LPWSADATA lpWSAData );
● wVersionRequested