getaddrinfo 函式詳解

2021-05-24 21:35:32 字數 4035 閱讀 4389

1. 概述

ipv4中使用gethostbyname()函式

完成主機名到位址解析,這個函式僅僅支援ipv4,且不允許呼叫者指定所需位址型別的任何資訊,返回的結構只包含了用於儲存ipv4位址的空間。ipv6中引入了getaddrinfo()的新api,它是協議無關的,既可用於ipv4也可用於ipv6。getaddrinfo函式能夠處理名字到位址以及服務到埠這兩種轉換,返回的是乙個addrinfo的結構(列表)指標而不是乙個位址清單。這些addrinfo結構隨後可由套介面函式直接使用。如此以來,getaddrinfo函式把協議相關性安全隱藏在這個庫函式內部。應用程式只要處理由getaddrinfo函式填寫的套介面位址結構。該函式在 posix規範中定義了。

2. 函式說明

包含標頭檔案

#include

函式原型

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

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>

intmain(

intargc, 

char

**argv)

struct

addrinfo 

*answer, hint, 

*curr;

char

ipstr[

16];   

bzero(

&hint, 

sizeof

(hint));

hint.ai_family 

=af_inet;

hint.ai_socktype 

=sock_stream;

intret 

=getaddrinfo(argv[

1], null, 

&hint, 

&answer);

if(ret !=0

) for

(curr 

=answer; curr 

!=null; curr 

=curr

->

ai_next) 

freeaddrinfo(answer);

exit(0);

}

getaddrinfo 函式詳解

getaddrinfo 函式詳解 1.概述 ipv4中使用 gethostbyname 函式 完成主機名到位址解析,這個函式僅僅支援ipv4,且不允許呼叫者指定所需位址型別的任何資訊,返回的結構只包含了用於儲存ipv4位址的空間。ipv6中引入了getaddrinfo 的新api,它是協議無關的,既...

getaddrinfo()函式詳解

gethostbyname和gethostbyaddr這兩個函式僅僅支援ipv4,getaddrinfo 函式能夠處理名字到位址以及服務到埠這兩種轉換,返回的是乙個sockaddr結構的鍊錶而不是乙個位址清單。這些sockaddr結構隨後可由套介面函式直接使用。如此以來,getaddrinfo函式把...

getaddrinfo 函式詳解

getaddrinfo 函式詳解 1.概述 ipv4中使用 gethostbyname 函式完成主機名到位址解析,這個函式僅僅支援ipv4,且不允許呼叫者指定所需位址型別的任何資訊,返回的結構只包含了用於儲存ipv4位址的空間。ipv6中引入了getaddrinfo 的新api,它是協議無關的,既可...