IPv4中使用gethostbyname()函數(shù)完成主機(jī)名到地址解析,但是該API不允許調(diào)用者指定
所需地址類(lèi)型的任何信息,返回的結(jié)構(gòu)只包含了用于存儲(chǔ)IPv4地址的空間。為了解決該問(wèn)題,IPv6中引入了getaddrinfo()的新API,它是
協(xié)議無(wú)關(guān)的,既可用于IPv4也可用于IPv6。調(diào)用該函數(shù)會(huì)獲得一個(gè)addrinfo結(jié)構(gòu)的列表,調(diào)用的返回值是addrinfo的結(jié)構(gòu)(列表)指針。
??????? 本文結(jié)合在WinowsXP和Windows2003
Server上使用該函數(shù)的經(jīng)驗(yàn),對(duì)getaddrinfo函數(shù)和addrinfo數(shù)據(jù)結(jié)構(gòu)進(jìn)行介紹,并對(duì)其參數(shù)的設(shè)置加以討論,主要包括
nodename和servname的取值對(duì)返回值的影響,hints成員變量的設(shè)置對(duì)返回值的影響等。
可能有不完全或不準(zhǔn)確的地方,歡迎大家討論并指出。
1
.getaddrinfo函
數(shù)原型
函數(shù)
|
參數(shù)說(shuō)明
|
int getaddrinfo(
const char* nodename
const char* servname,
const struct addrinfo* hints,//
struct addrinfo** res
);
|
nodename:節(jié)點(diǎn)名可以是主機(jī)名,也可以是數(shù)字地址。(IPV4的10進(jìn)點(diǎn)分,或是IPV6的16進(jìn)制)
servname:包含十進(jìn)制數(shù)的端口號(hào)或服務(wù)名如(ftp,http)
hints:是一個(gè)空指針或指向一個(gè)addrinfo結(jié)構(gòu)的指針,由調(diào)用者填寫(xiě)關(guān)于它所想返回的信息類(lèi)型的線索。
res:存放返回addrinfo結(jié)構(gòu)鏈表的指針
|
Getaddrinfo提供獨(dú)立于協(xié)議的名稱解析。
函數(shù)的前兩個(gè)參數(shù)分別是節(jié)點(diǎn)名和服務(wù)名。節(jié)點(diǎn)名可以是主機(jī)名,也可以是地址串(IPv4的點(diǎn)分十進(jìn)制數(shù)表示或IPv6的十六進(jìn)制數(shù)字串)。服務(wù)名可
以是十進(jìn)制的端口號(hào),也可以是已定義的服務(wù)名稱,如ftp、http等。注意:其中節(jié)點(diǎn)名和服務(wù)名都是可選項(xiàng),即節(jié)點(diǎn)名或服務(wù)名可以為NULL,
此時(shí)調(diào)用的結(jié)果將取缺省設(shè)置,后面將詳細(xì)討論。
函數(shù)的第三個(gè)參數(shù)hints是addrinfo結(jié)構(gòu)的指針,由調(diào)用者填寫(xiě)關(guān)于它所想返回的信息類(lèi)型的線索。函數(shù)的返回值是一個(gè)指向addrinfo
結(jié)構(gòu)的鏈表指針res。
2
.addrinfo結(jié)構(gòu)
結(jié)構(gòu)
|
固定的參數(shù)
|
typedef struct addrinfo {??
int ai_flags;??
int ai_family;??
int ai_socktype;??
int ai_protocol;??
size_t ai_addrlen;??
char* ai_canonname;??
struct sockaddr* ai_addr;??
struct addrinfo* ai_next;
}
|
ai_addrlen must be zero or a null pointer
ai_canonname must be zero or a null pointer
ai_addr must be zero or a null pointer
ai_next must be zero or a null pointer
|
可以改動(dòng)的參數(shù)
|
ai_flags:AI_PASSIVE,AI_CANONNAME,AI_NUMERICHOST
ai_family: AF_INET,AF_INET6
ai_socktype:SOCK_STREAM,SOCK_DGRAM
ai_protocol:IPPROTO_IP, IPPROTO_IPV4, IPPROTO_IPV6 etc.
|
3
參數(shù)說(shuō)明
在getaddrinfo函數(shù)之前通常需要對(duì)以下6個(gè)參數(shù)進(jìn)行以下設(shè)置:nodename、servname、hints的ai_flags、
ai_family、ai_socktype、ai_protocol
在6項(xiàng)參數(shù)中,對(duì)函數(shù)影響最大的是nodename,sername和hints.ai_flag
而ai_family只是有地址為v4地址或v6地址的區(qū)別。而ai_protocol一般是為0不作改動(dòng)。
其中ai_flags、ai_family、ai_socktype說(shuō)明如下:
參數(shù)
|
取值
|
值
|
說(shuō)明
|
ai_family
|
AF_INET
|
2
|
IPv4
|
AF_INET6
|
23
|
IPv6
|
AF_UNSPEC
|
0
|
協(xié)議無(wú)關(guān)
|
ai_protocol
|
IPPROTO_IP
|
0
|
IP協(xié)議
|
IPPROTO_IPV4
|
4
|
IPv4
|
IPPROTO_IPV6
|
41
|
IPv6
|
IPPROTO_UDP
|
17
|
UDP
|
IPPROTO_TCP
|
6
|
TCP
|
ai_socktype
|
SOCK_STREAM
|
1
|
流
|
SOCK_DGRAM
|
2
|
數(shù)據(jù)報(bào)
|
ai_flags
|
AI_PASSIVE
|
1
|
被動(dòng)的,用于bind,通常用于server socket
|
AI_CANONNAME
|
2
|
|
AI_NUMERICHOST
|
4
|
地址為數(shù)字串
|
對(duì)于ai_flags值的說(shuō)明:
AI_NUMERICHOST
|
AI_CANONNAME
|
AI_PASSIVE
|
0/1
|
0/1
|
0/1
|
如上表所示,ai_flagsde值范圍為0~7,取決于程序如何設(shè)置3個(gè)標(biāo)志位,比如設(shè)置ai_flags為
“AI_PASSIVE|AI_CANONNAME”,ai_flags值就為3。三個(gè)參數(shù)的含義分別為:
(1)AI_PASSIVE當(dāng)此標(biāo)志置位時(shí),表示調(diào)用者將在bind()函數(shù)調(diào)用中使用返回的地址結(jié)構(gòu)。當(dāng)此標(biāo)志不置位時(shí),表示將在
connect()函數(shù)調(diào)用中使用。
當(dāng)節(jié)點(diǎn)名位NULL,且此標(biāo)志置位,則返回的地址將是通配地址。
如果節(jié)點(diǎn)名NULL,且此標(biāo)志不置位,則返回的地址將是回環(huán)地址。
(2)AI_CANNONAME當(dāng)此標(biāo)志置位時(shí),在函數(shù)所返回的第一個(gè)addrinfo結(jié)構(gòu)中的ai_cannoname成員中,應(yīng)該包含一個(gè)以空
字符結(jié)尾的字符串,字符串的內(nèi)容是節(jié)點(diǎn)名的正規(guī)名。
(3)AI_NUMERICHOST當(dāng)此標(biāo)志置位時(shí),此標(biāo)志表示調(diào)用中的節(jié)點(diǎn)名必須是一個(gè)數(shù)字地址字符串。
4.實(shí)際使用的幾種常用設(shè)置
一般情況下,client/server編程中,server端調(diào)用bind(如果面向連接的還需要listen),client則不用掉bind
函數(shù),解析地址后直接connect(面向連接)或直接發(fā)送數(shù)據(jù)(無(wú)連接)。因此,比較常見(jiàn)的情況有
(1)???????
通常服務(wù)器端在調(diào)用getaddrinfo之前,ai_flags設(shè)置AI_PASSIVE,用于bind;主機(jī)名nodename通常會(huì)設(shè)置為
NULL,返回通配地址[::]。
(2)???????
客戶端調(diào)用getaddrinfo時(shí),ai_flags一般不設(shè)置AI_PASSIVE,但是主機(jī)名nodename和服務(wù)名servname(更愿意稱
之為端口)則應(yīng)該不為空。
(3)???????
當(dāng)然,即使不設(shè)置AI_PASSIVE,取出的地址也并非不可以被bind,很多程序中ai_flags直接設(shè)置為0,即3個(gè)標(biāo)志位都不設(shè)置,這種情況下
只要hostname和servname設(shè)置的沒(méi)有問(wèn)題就可以正確bind。
上述情況只是簡(jiǎn)單的client/server中的使用,但實(shí)際在使用getaddrinfo和參考國(guó)外開(kāi)源代碼的時(shí)候,曾遇到一些將
servname(即端口)設(shè)為NULL的情況
(當(dāng)然,此時(shí)nodename必不為NULL,否則調(diào)用getaddrinfo會(huì)報(bào)錯(cuò))。以下分情況進(jìn)行了測(cè)試:
(1)??????? 如果nodename是字符串型的IPv6地址,bind的時(shí)候會(huì)分配臨時(shí)端口;
(2)???????
如果nodename是本機(jī)名,servname為NULL,則根據(jù)操作系統(tǒng)的不同略有不同,本文僅在WinXP和Win2003上作了測(cè)試。
a)????????? WinXP系統(tǒng)(SP2)返回loopback地址[::1]
b)????????
Win2003則將本機(jī)的所有IPv6地址列表加以返回。因?yàn)橥ǔR慌_(tái)IPv6主機(jī)都有可能不止一個(gè)IPv6地址,比如fe80::1(本機(jī)
loopback地址)、fe80::***的Link-Local地址、3ffe:***的全局地址等等。這種情況下調(diào)用getaddrinfo會(huì)將這
些地址全部返回,調(diào)用者應(yīng)該注意如何使用這些地址。另外要注意的是,對(duì)于fe80::的地址在綁定的時(shí)候必須標(biāo)明接口地址,即使用
fe80::20d:60ff:fe78:51c2%4或fe80::1%1這樣的地址格式,通過(guò)getaddrinfo直接取出fe80地址好像無(wú)法直
接bind。