getaddrinfo

gethostbyname和gethostbyaddr這兩個函式僅僅支持IPv4,getaddrinfo函式能夠處理名字到地址以及服務到連線埠這兩種轉換,返回的是一個sockaddr結構的鍊表而不是一個地址清單。這些sockaddr結構隨後可由套接口函式直接使用。如此一來,getaddrinfo函式把協定相關性安全隱藏在這個庫函式內部。應用程式只要處理由getaddrinfo函式填寫的套接口地址結構。該函式在 POSIX規範中定義了。

基本介紹

  • 外文名:getaddrinfo
  • 特點:支持IPv4 和IPv6
  • 目的:該函式在 POSIX規範中定義了。
  • 屬於:可由套接口函式直接使用
函式說明,參數設定,參數說明,ai_flags值的說明,實際常用設定,函式使用示例,

函式說明

包含頭檔案
Linux下為#include<netdb.h>
windows下為#include <ws2tcpip.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——出錯
數據結構說明
addrinfo類型說明:
struct addrinfo {
int ai_flags;
int ai_family;
int ai_socktype;
int ai_protocol;
socklen_t ai_addrlen; //ai_addr的地址長度
struct sockaddr *ai_addr;
char *ai_canonname;
struct addrinfo *ai_next; //指向鍊表的下一個結點
};

參數設定

在getaddrinfo函式之前通常需要對以下6個參數進行以下設定:nodename、servname、hints的ai_flags、ai_family、ai_socktype、ai_protocol。
在6項參數中,對函式影響最大的是nodename,sername和 h i n t s . ai_flag,而ai_family只是有地址為v4地址或v6地址的區別。ai_protocol一般是為0不作改動。
getaddrinfo在實際使用中的幾種常用參數設定

參數說明

在getaddrinfo函式之前通常需要對以下6個參數進行以下設定:nodename、servname、hints的ai_flags、ai_family、ai_socktype、ai_protocol
在6項參數中,對函式影響最大的是nodename,sername和hints
而ai_family只是有地址為v4地址或v6地址的區別。而ai_protocol一般是為0不作改動。
其中ai_flags、ai_family、ai_socktype說明如下:
參數
取值

說明
ai_family
AF_INET
2
IPv4
AF_INET6
23
IPv6
AF_UNSPEC
0
協定無關
ai_protocol
IPPROTO_IP
0
IP協定
IPPROTO_IPV4
4
IPv4
IPPROTO_IPV6
41
IPv6
IPPROTO_UDP
17
UDP
IPPROTO_TCP
6
TCP
ai_socktype
SOCK_STREAM
1

SOCK_DGRAM
2
ai_flags
AI_PASSIVE
1
被動的,用於bind,通常用於server socket
AI_CANONNAME
2
用於返回主機的規範名稱
AI_NUMERICHOST
4
地址為數字串

ai_flags值的說明

AI_NUMERICHOST
AI_CANONNAME
AI_PASSIVE
0/1
0/1
0/1
如上表所示,ai_flags的值的範圍為0~7,取決於程式如何設定3個標誌位,比如設定ai_flags為“AI_PASSIVE|AI_CANONNAME”,ai_flags值就為3。三個參數的含義分別為:
(1)AI_PASSIVE當此標誌置位時,表示調用者將在bind()函式調用中使用返回的地址結構。當此標誌不置位時,表示將在connect()函式調用中使用。
當節點名位NULL,且此標誌置位,則返回的地址將是通配地址。
如果節點名NULL,且此標誌不置位,則返回的地址將是迴環地址。
(2)AI_CANNONAME當此標誌置位時,在函式所返回的第一個addrinfo結構中的ai_cannoname成員中,應該包含一個以空字元結尾的字元串,字元串的內容是節點名的正規名。
(3)AI_NUMERICHOST當此標誌置位時,此標誌表示調用中的節點名必須是一個數字地址字元串。

實際常用設定

一般情況下,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。

函式使用示例

getaddrinfo函式是gethostbyname的替代函式,其支持IPV4和IPV6協定,簡單使用示例如下所示。
#include "stdafx.h"#include <winsock2.h>#include <ws2tcpip.h>#include <stdio.h>#pragma comment (lib, "Ws2_32.lib")int  main(int argc, char **argv){    WORD wVersion;    WSADATA WSAData;    wVersion=MAKEWORD(2,2);    WSAStartup(wVersion,&WSAData);    struct addrinfo hints;    struct addrinfo *res, *cur;    int ret;    struct sockaddr_in *addr;    char m_ipaddr[16];    memset(&hints, 0, sizeof(struct addrinfo));    hints.ai_family = AF_INET;     /* Allow IPv4 */    hints.ai_flags = AI_PASSIVE;/* For wildcard IP address */    hints.ai_protocol = 0;         /* Any protocol */    hints.ai_socktype = SOCK_STREAM;           ret = getaddrinfo("cplusplus_me", NULL,&hints,&res);        if (ret == -1) {        perror("getaddrinfo");        exit(1);    }    for (cur = res; cur != NULL; cur = cur->ai_next) {        addr = (struct sockaddr_in *)cur->ai_addr;        sprintf(m_ipaddr, "%d.%d.%d.%d",        (*addr).sin_addr.S_un.S_un_b.s_b1,        (*addr).sin_addr.S_un.S_un_b.s_b2,        (*addr).sin_addr.S_un.S_un_b.s_b3,        (*addr).sin_addr.S_un.S_un_b.s_b4);        printf("%s\n",m_ipaddr);    }    freeaddrinfo(res);    return 0;}

相關詞條

熱門詞條

聯絡我們