getaddrinfo()函數詳解
?
1. 概述IPv4中使用 gethostbyname()函數 完成主機名到地址解析,這個函數僅僅支持IPv4,且不允許調用者指定所需地址類型的任何信息,返回的結構只包含了用于存儲IPv4地址的空間。IPv6中引入了getaddrinfo()的新API,它是協議無關的,既可用于IPv4也可用于IPv6。getaddrinfo函數能夠處理名字到地址以及服務到端口這兩種轉換,返回的是一個addrinfo的結構(列表)指針而不是一個地址清單。這些addrinfo結構隨后可由套接口函數直接使用。如此以來,getaddrinfo函數把協議相關性安全隱藏在這個庫函數內部。應用程序只要處理由getaddrinfo函數填寫的套接口地址結構。該函數在 POSIX規范中定義了。
2. 函數說明
包含頭文件
#include<netdb.h>
函數原型
int getaddrinfo( const char *hostname, const char *service, const struct addrinfo *hints, struct addrinfo **result );
參數說明
hostname:一個主機名或者地址串(IPv4的點分十進制串或者IPv6的16進制串)
service:服務名可以是十進制的端口號,也可以是已定義的服務名稱,如ftp、http等
hints:可以是一個空指針,也可以是一個指向某個 addrinfo結構體 的指針,調用者在這個結構中填入關于期望返回的信息類型的暗示。舉例來說:如果指定的服務既支持TCP也支持UDP,那么調用者可以把hints結構中的ai_socktype成員設置成SOCK_DGRAM使得返回的僅僅是適用于數據報套接口的信息。
result:本函數通過result指針參數返回一個指向addrinfo結構體鏈表的指針。
返回值:0——成功,非0——出錯
3. 參數設置
在getaddrinfo函數之前通常需要對以下6個參數進行以下設置:nodename、servname、hints的ai_flags、ai_family、ai_socktype、ai_protocol。
在6項參數中,對函數影響最大的是nodename,sername和hints.ai_flag,而ai_family只是有地址為v4地址或v6地址的區別。ai_protocol一般是為0不作改動。
getaddrinfo在實際使用中的幾種常用參數設置
一般情況下,client/server編程中,server端調用bind(如果面向連接的還需要listen),client則不用掉bind函數,解析地址后直接connect(面向連接)或直接發送數據(無連接)。因此,比較常見的情況有
(1)??? 通常服務器端在調用getaddrinfo之前,ai_flags設置AI_PASSIVE,用于bind;主機名nodename通常會設置為NULL,返回通配地址[::]。
(2)??? 客戶端調用getaddrinfo時,ai_flags一般不設置AI_PASSIVE,但是主機名nodename和服務名servname(更愿意稱之為端口)則應該不為空。
(3)??? 當然,即使不設置AI_PASSIVE,取出的地址也并非不可以被bind,很多程序中ai_flags直接設置為0,即3個標志位都不設置,這種情況下只要hostname和servname設置的沒有問題就可以正確bind。
上述情況只是簡單的client/server中的使用,但實際在使用getaddrinfo和參考國外開源代碼的時候,曾遇到一些將servname(即端口)設為NULL的情況(當然,此時nodename必不為NULL,否則調用getaddrinfo會報錯)。
以下分情況進行了測試:
(1)??? 如果nodename是字符串型的IPv6地址,bind的時候會分配臨時端口;
(2)??? 如果nodename是本機名,servname為NULL,則根據操作系統的不同略有不同,本文僅在WinXP和Win2003上作了測試。
??????? a)??? WinXP系統(SP2)返回loopback地址[::1]
??????? b)??? Win2003則將本機的所有IPv6地址列表加以返回。因為通常一臺IPv6主機都有可能不止一個IPv6地址,比如fe80::1(本機 loopback地址)、fe80::***的Link-Local地址、3ffe:***的全局地址等等。這種情況下調用getaddrinfo會將這些地址全部返回,調用者應該注意如何使用這些地址。另外要注意的是,對于fe80::的地址在綁定的時候必須標明接口地址,即使用 fe80::20d:60ff:fe78:51c2%4或fe80::1%1這樣的地址格式,通過getaddrinfo直接取出fe80地址好像無法直接bind。
?? ??? ?
?? ??? ?
4. 使用細節
如果本函數返回成功,那么由result參數指向的變量已被填入一個指針,它指向的是由其中的ai_next成員串聯起來的addrinfo結構鏈表。可以導致返回多個addrinfo結構的情形有以下2個:
??? 1.??? 如果與hostname參數關聯的地址有多個,那么適用于所請求地址簇的每個地址都返回一個對應的結構。
??? 2.??? 如果service參數指定的服務支持多個套接口類型,那么每個套接口類型都可能返回一個對應的結構,具體取決于hints結構的ai_socktype成員。
我們必須先分配一個hints結構,把它清零后填寫需要的字段,再調用getaddrinfo,然后遍歷一個鏈表逐個嘗試每個返回地址。
?? ??? ?
getaddrinfo解決了把主機名和服務名轉換成套接口地址結構的問題。
其中,如果getaddrinfo出錯,那么返回一個非0的錯誤值。
#include<netdb.h>
const char *gai_strerror( int error );
該函數以getaddrinfo返回的非0錯誤值的名字和含義為他的唯一參數,返回一個指向對應的出錯信息串的指針。
由getaddrinfo返回的所有存儲空間都是動態獲取的,這些存儲空間必須通過調用freeaddrinfo返回給系統。
#include< netdb.h >
void freeaddrinfo( struct addrinfo *ai );
ai參數應指向由getaddrinfo返回的第一個addrinfo結構。這個連表中的所有結構以及它們指向的任何動態存儲空間都被釋放掉。
5. 例子

#include?
<
stdio.h
>
#include? < stdlib.h >
#include? < sys / socket.h >
#include? < netinet / in .h >
#include? < netdb.h >
#include? < string .h >
int ?main( int ?argc,? char ? ** argv)
{
if ?(argc? != ? 2 )?{
fprintf(stderr,? " Usage:?%s?hostname\n " ,
argv[ 1 ]);
exit( 1 );???
}
struct ?addrinfo? * answer,?hint,? * curr;
char ?ipstr[ 16 ];???
bzero( & hint,? sizeof (hint));
hint.ai_family? = ?AF_INET;
hint.ai_socktype? = ?SOCK_STREAM;
int ?ret? = ?getaddrinfo(argv[ 1 ],?NULL,? & hint,? & answer);
if ?(ret? != ? 0 )?{
fprintf(stderr, " getaddrinfo:?&s\n " ,
gai_strerror(ret));
exit( 1 );
}
for ?(curr? = ?answer;?curr? != ?NULL;?curr? = ?curr -> ai_next)?{
inet_ntop(AF_INET,
& ((( struct ?sockaddr_in? * )(curr -> ai_addr)) -> sin_addr),
ipstr,? 16 );
printf( " %s\n " ,?ipstr);
}
freeaddrinfo(answer);
exit( 0 );
}
#include? < stdlib.h >
#include? < sys / socket.h >
#include? < netinet / in .h >
#include? < netdb.h >
#include? < string .h >
int ?main( int ?argc,? char ? ** argv)
{
if ?(argc? != ? 2 )?{
fprintf(stderr,? " Usage:?%s?hostname\n " ,
argv[ 1 ]);
exit( 1 );???
}
struct ?addrinfo? * answer,?hint,? * curr;
char ?ipstr[ 16 ];???
bzero( & hint,? sizeof (hint));
hint.ai_family? = ?AF_INET;
hint.ai_socktype? = ?SOCK_STREAM;
int ?ret? = ?getaddrinfo(argv[ 1 ],?NULL,? & hint,? & answer);
if ?(ret? != ? 0 )?{
fprintf(stderr, " getaddrinfo:?&s\n " ,
gai_strerror(ret));
exit( 1 );
}
for ?(curr? = ?answer;?curr? != ?NULL;?curr? = ?curr -> ai_next)?{
inet_ntop(AF_INET,
& ((( struct ?sockaddr_in? * )(curr -> ai_addr)) -> sin_addr),
ipstr,? 16 );
printf( " %s\n " ,?ipstr);
}
freeaddrinfo(answer);
exit( 0 );
}
?
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
