函式說明
包含頭檔案
windows下為#include <ws2tcpip.h>
int getaddrinfo( const char *hostname, const char *service, const struct addrinfo *hints, struct addrinfo **result );
參數說明
service:服務名可以是十進制的
連線埠號,也可以是已定義的服務名稱,如ftp、http等
hints:可以是一個空
指針,也可以是一個指向某個addrinfo
結構體的指針,調用者在這個結構中填入關於期望返回的信息類型的暗示。舉例來說:指定的服務既可支持
TCP也可支持
UDP,所以調用者可以把hints結構中的ai_socktype成員設定成
SOCK_DGRAM使得返回的僅僅是適用於
數據報套接口的信息。
result:本函式通過result指針參數返回一個指向addrinfo結構體
鍊表的
指針。
參數設定
在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當此標誌置位時,此標誌表示調用中的節點名必須是一個
數字地址字元串。
實際常用設定
(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上作了測試。
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;}