int getaddrinfo(const char *domain, const char *service, const struct addrinfo *hints, struct addrinfo **res); struct addrinfo { int ai_flags; int ai_family; int ai_socktype; int ai_protocol; socklen_t ai_addrlen; struct sockaddr *ai_addr; char *ai_canonname; struct addrinfo *ai_next; };
查询给定的域名 domain,与服务名 service,并返回对应的IP地址与端口号.
domain;可以是域名,也可以是IP地址的点分十进制表示,或者是0;
service;可以是服务名,如"http","ftp";也可以是端口号的十进制字符串,如"80","23";或者是0
若取0,则查询结果中的端口号是未设置的,此时还需手动赋值!
hints,指向着一个 addrinfo 结构体,该结构体除了以下指定的域外,其他域应该取值为0,或者NULL;
ai_family;用来限定 getaddrinfo() 的查询结果中 ai_family 的取值;可以是 AF_INET(此时 getaddrinfo() 查询结果中 ai_family 的取值只能是 AF_INET),或者 AF_INET6,或者 AF_UNSPEC(不限定查询结果中ai_family的取值);
ai_socktype;用来限定查询结果中 ai_socktype 的取值;可以是 SOCK_STREAM,SOCK_DGRAM;或者为0,表示不限定;
ai_protocol;用来限定查询结果中 ai_protocol 的取值;若为0,则表示不限定
ai_flags;位掩码,可以是以下值,或者它们或运算的结果:
AI_NUMERICHOST;此时表明 domain 是IP地址的点分十进制;不是域名,此时不会执行DNS查询.
AI_NUMERICSERV;此时表明 service 只是端口号的字符串表示;不是服务名,
AI_PASSIVE;仅当 domain == 0 时起作用;当 domain==0 时,若设置了该标志,则查询结果中的IP地址是通配地址;若未设置该标志,则IP地址是环回地址.
AI_CANONNAME;若设置了该标志,则 *res->ai_canonname 域中存放着目的主机(即 domain 指定的主机)的 official name(我理解为完全限定域名).
AI_ADDRCONFIG;若设置了该标志,则仅当本地主机至少配置了一个IPV4(或IPV6)地址时,才会在返回的查询结果中包含IPV4(或IPV6)地址;如下:
域名 对应着如下 IP 地址: 173.194.127.180 173.194.127.176 2404:6800:4005:802::1010 若本地主机仅配置了 IPV4 地址,则返回的查询结果中不包含 IPV6 地址,即此时只有: 173.194.127.180 173.194.127.176 同样若本地主机仅配置了 IPV6 地址,则返回的查询结果中仅包含IPV6地址.
若 hints==0,则相当于 ai_family==AF_UNSPEC,ai_socktype==0,ai_protocol==0,ai_flags==AI_ADDRCONFIG|AI_V4MAPPED.
res;getaddrinfo() 返回的查询结果是一个单链表,链表中每一个元素类型为 struct addrinfo;链表的第一个元素的指针存放在 *res 中.
返回值;若不为0,则表明查询出错,此时返回值表示出错码;返回0,表示查询成功.
ByteArray family_to_str(int family){ if(family == AF_INET) return "AF_INET"; if(family == AF_INET6) return "AF_INET6"; ByteArray result("Unknow"); result.appendFormat("(%d)",family); return result; } ByteArray socktype_to_str(int socktype){ if(socktype == SOCK_STREAM) return "SOCK_STREAM"; if(socktype == SOCK_DGRAM) return "SOCK_DGRAM"; ByteArray result("Unknow"); result.appendFormat("(%d)",socktype); return result; } ByteArray procotol_to_str(int proto){ if(proto == IPPROTO_TCP) return "TCP"; if(proto == IPPROTO_UDP) return "UDP"; ByteArray result("Unknow"); result.appendFormat("(%d)",proto); return result; } void print_addrinfo(const struct addrinfo *addr_info){ printf("faimly: %s\nsocktype: %s\nprotocol: %s\ncannoname: %s\n" ,family_to_str(addr_info->ai_family).constData() ,socktype_to_str(addr_info->ai_socktype).constData() ,procotol_to_str(addr_info->ai_protocol).constData() ,addr_info->ai_canonname); if(addr_info->ai_addr->sa_family == AF_INET){ struct sockaddr_in *addr_ptr = (struct sockaddr_in*)addr_info->ai_addr; char buf[INET_ADDRSTRLEN]; if(inet_ntop(AF_INET,&addr_ptr->sin_addr,buf,INET_ADDRSTRLEN) == 0) SystemCallFailed(inet_ntop); printf("ipv4: %s\nport: %d\n",buf,ntohs(addr_ptr->sin_port)); }else if(addr_info->ai_addr->sa_family == AF_INET6){ struct sockaddr_in6 *addr_ptr = (struct sockaddr_in6*)addr_info->ai_addr; char buf[INET6_ADDRSTRLEN]; if(inet_ntop(AF_INET6,&addr_ptr->sin6_addr,buf,INET6_ADDRSTRLEN) == 0) SystemCallFailed(inet_ntop); printf("ipv6: %s\nport: %d\n",buf,ntohs(addr_ptr->sin6_port)); }else printf("ip: Unknow\nport: Unknow\n"); } int main(int argc,char *argv[]){ if(argc < 3){ printf("%s 域名/IP地址 服务名/端口号\n",argv[0]); return 1; } int core_ret; struct addrinfo *result; struct addrinfo hints; memset(&hints,0,sizeof(hints)); hints.ai_flags = AI_CANONNAME; core_ret = getaddrinfo(argv[1],argv[2],&hints,&result); if(core_ret != 0) ThrowException(0,"func=getaddrinfo;ret_code=%d;err_str=%s",core_ret,gai_strerror(core_ret)); while(result){ print_addrinfo(result); putchar('\n'); result = result->ai_next; } return 0; }
# 程序运行结果 # 此时域名 m 解析后得到一个 IPV4 地址.服务名 http 解析后得到一个端口号,但支持 TCP,UDP 2种协议.所以总可能数:1(1种IP地址)*2(2种协议)=2 # 将根据解析出的IP地址的类型设置 ai_family 域(即若IP地址是IPV4地址,则为 AF_INET,若为IPV6地址,则设置为 AF_INET6). # 根据服务名支持的协议设置 ai_socktype,ai_protocol; $ ./Test www.so.com http faimly: AF_INET socktype: SOCK_STREAM protocol: TCP cannoname: so.qh-lb.com ipv4: 101.4.60.8 port: 80 faimly: AF_INET socktype: SOCK_DGRAM protocol: UDP cannoname: (null) ipv4: 101.4.60.8 port: 80 #此时域名 解析后得到一个 IPV4 地址;服务名 ftp 解析后得到一个端口号,并且只支持 TCP 协议,所以总可能数: 1*1=1; $ ./Test www.360.cn ftp faimly: AF_INET socktype: SOCK_STREAM protocol: TCP cannoname: www.360.cn ipv4: 101.4.60.193 port: 21
void freeaddrinfo(struct addrinfo *res);
释放 getaddrinfo() 返回的单链表.
const char *gai_strerror(int errcode);