主机名与 IP 地址的转换(续)
程序员文章站
2022-07-12 16:49:11
...
在主机名与 IP 地址的转换一节中提到的 gethostbyname 和 gethostbyaddr 函数仅仅支持 IPv4,本节将介绍一个同时支持 IPv4 和 IPv6 的 getaddrinfo 函数,它能够处理名字到地址以及服务到端口这两种转换。
其中,hostname 参数是一个主机名,也可以是一个 IPv4 点分十进制或者 IPv6 十六进制数串。service 参数是一个服务名或十进制端口号数串。函数最终通过 result 参数返回一个指向 addrinfo 结构链表的指针,该链表是由 getaddrinfo 函数动态分配的,之后各个 addrinfo 结构中的 ai_addr 成员就可直接用于后续的 socket 等函数中。
hints 参数可以是一个空指针,也可以是一个指向某个 addrinfo 结构的指针,调用者可以在该结构中填入关于期望返回的信息类型的暗示。比如,如果指定的服务既支持 TCP 也支持 UDP,则可以把该结构中的 ai_socktype 成员设置为 SOCK_DGRAM,使得返回的仅仅是适用于数据报套接字的信息。
hints 结构中调用者可以设置的成员有:
* ai_flags(零个或多个 AI_xxx 值的并集)
* ai_family(某个 AF_xxx 值)
* ai_socktype(某个 SOCK_xxx 值)
* ai_protocol
其中 ai_flags 成员可用的标志值及其含义如下。
1、AI_PASSIVE:套接字将用于被动打开。
2、AI_CANONNAME:告知 getaddrinfo 函数返回主机的规范名字,此时该函数返回的第一个 addrinfo 结构的 ai_canonname 成员就指向所查找主机的规范名字。
3、AI_NUMERICHOST:只允许 hostname 参数是一个地址数串。
4、AI_NUMERICSERV:只允许 service 参数是一个十进制数串。
5、AI_V4MAPPED:如果同时指定 ai_family 成员的值为 AF_INET6,那么当没有可用的 AAAA 记录时就返回与 A 记录对应的 IPv4 映射的 IPv6 地址。
6、AI_ALL:如果同时指定 AI_V4MAPPED 标志,则除了返回与 AAAA 记录对应的 IPv6 地址外,还返回与 A 记录对应的 IPv4 映射的 IPv6 地址。
7、AI_ADDRCONFIG:按照所在主机的配置选择返回地址类型,也就是只查找与所在主机回馈接口以外的网络接口配置的 IP 地址版本一致的地址。
如果 hints 参数是一个空指针,本函数就假设 ai_flags、ai_socktype 和 ai_protocol 的值均为 0,而 ai_family 的值为 AF_UNSPEC。
为便于理解,假设有台主机 freebsd4 的规范名字是“freebsd4.unpbook.com”,并且它在 DNS(服务名是 domain,端口是 53)中有 2 个 IPv4 地址,那么在执行了如下的程序片段后:
res 变量将指向一个由 getaddrinfo 函数动态分配的 addrinfo 结构链表,如下图所示。
getaddrinfo 函数出错时会返回非 0 错误值,可利用 gai_strerror 函数来获取对应的描述信息。下表总结了这些错误值的名字和含义。
getaddrinfo 返回的所有存储空间都是动态分配的,这些空间可使用 freeaddrinfo 函数释放,它的 ai 参数应指向由 getaddrinfo 函数返回的第一个 addrinfo 结构,因此在调用 freeaddrinfo 函数后,就一定不能再使用这些空间中保存的数据。
和 getaddrinfo 函数相对应的是 getnameinfo 函数,它以一个套接字地址结构作为参数,返回描述其中的主机的一个字符串和描述其中的服务的另一个字符串。它也是协议无关的。
这里,sockaddr 参数指向包含待转换的协议地址结构,addrlen 是该结构的长度。host 参数和 serv 参数可分别代表用来存储返回的主机和服务字符串,它们都需要由调用者预先分配存储空间。如果不想返回主机或服务字符串,可将 hostlen 或 servlen 参数指定为 0。
flags 参数可以用于改变 getnameinfo 的操作,可指定的值及其说明如下图所示。
在此需要注意的是,当调用者知道要处理的是数据报套接字时,应设置 NI_DGRAM 标志,因为套接字地址结构中仅给出了 IP 地址和端口号,getnameinfo 无法就此确定所用的协议。
#include <netdb.h> int getaddrinfo(const char *hostname, const char *service, const struct addrinfo *hints, struct addrinfo **result); /* 返回值:若成功返回 0,否则返回非 0 */ const char *gai_strerror(int error); /* 返回:指向错误描述消息的指针 */ void freeaddrinfo(struct addrinfo *ai); struct addrinfo{ int ai_flags; // AI_PASSIVE, AI_CANONNAME int ai_family; // AF_xxx int ai_socktype; // SOCK_xxx int ai_protocol; // 0 or IPPROTO_xxx for IPv4 and IPv6 socklen_t ai_addrlen; // length of ai_addr char *ai_canonname; // ptr to canonical name for host struct sockaddr *ai_addr; // ptr to socket address structure struct addrinfo *ai_next; // ptr to next structure in linked list };
其中,hostname 参数是一个主机名,也可以是一个 IPv4 点分十进制或者 IPv6 十六进制数串。service 参数是一个服务名或十进制端口号数串。函数最终通过 result 参数返回一个指向 addrinfo 结构链表的指针,该链表是由 getaddrinfo 函数动态分配的,之后各个 addrinfo 结构中的 ai_addr 成员就可直接用于后续的 socket 等函数中。
hints 参数可以是一个空指针,也可以是一个指向某个 addrinfo 结构的指针,调用者可以在该结构中填入关于期望返回的信息类型的暗示。比如,如果指定的服务既支持 TCP 也支持 UDP,则可以把该结构中的 ai_socktype 成员设置为 SOCK_DGRAM,使得返回的仅仅是适用于数据报套接字的信息。
hints 结构中调用者可以设置的成员有:
* ai_flags(零个或多个 AI_xxx 值的并集)
* ai_family(某个 AF_xxx 值)
* ai_socktype(某个 SOCK_xxx 值)
* ai_protocol
其中 ai_flags 成员可用的标志值及其含义如下。
1、AI_PASSIVE:套接字将用于被动打开。
2、AI_CANONNAME:告知 getaddrinfo 函数返回主机的规范名字,此时该函数返回的第一个 addrinfo 结构的 ai_canonname 成员就指向所查找主机的规范名字。
3、AI_NUMERICHOST:只允许 hostname 参数是一个地址数串。
4、AI_NUMERICSERV:只允许 service 参数是一个十进制数串。
5、AI_V4MAPPED:如果同时指定 ai_family 成员的值为 AF_INET6,那么当没有可用的 AAAA 记录时就返回与 A 记录对应的 IPv4 映射的 IPv6 地址。
6、AI_ALL:如果同时指定 AI_V4MAPPED 标志,则除了返回与 AAAA 记录对应的 IPv6 地址外,还返回与 A 记录对应的 IPv4 映射的 IPv6 地址。
7、AI_ADDRCONFIG:按照所在主机的配置选择返回地址类型,也就是只查找与所在主机回馈接口以外的网络接口配置的 IP 地址版本一致的地址。
如果 hints 参数是一个空指针,本函数就假设 ai_flags、ai_socktype 和 ai_protocol 的值均为 0,而 ai_family 的值为 AF_UNSPEC。
为便于理解,假设有台主机 freebsd4 的规范名字是“freebsd4.unpbook.com”,并且它在 DNS(服务名是 domain,端口是 53)中有 2 个 IPv4 地址,那么在执行了如下的程序片段后:
struct addrinfo hints, *res; zero(&hints, sizeof(hints)); hints.ai_flags = AI_CANONNAME; hints.ai_family = AF_INET; getaddrinfo("freebsd4", "domain", &hints, &res);
res 变量将指向一个由 getaddrinfo 函数动态分配的 addrinfo 结构链表,如下图所示。
getaddrinfo 函数出错时会返回非 0 错误值,可利用 gai_strerror 函数来获取对应的描述信息。下表总结了这些错误值的名字和含义。
getaddrinfo 返回的所有存储空间都是动态分配的,这些空间可使用 freeaddrinfo 函数释放,它的 ai 参数应指向由 getaddrinfo 函数返回的第一个 addrinfo 结构,因此在调用 freeaddrinfo 函数后,就一定不能再使用这些空间中保存的数据。
和 getaddrinfo 函数相对应的是 getnameinfo 函数,它以一个套接字地址结构作为参数,返回描述其中的主机的一个字符串和描述其中的服务的另一个字符串。它也是协议无关的。
#include <netdb.h> int getnameinfo(const struct sockaddr *sockaddr, socklen_t addrlen, char *host, socklen_t hostlen, char *serv, socklen_t servlen, int flags); /* 返回值:若成功则为 0,否则为非 0(同 getaddrinfo) */
这里,sockaddr 参数指向包含待转换的协议地址结构,addrlen 是该结构的长度。host 参数和 serv 参数可分别代表用来存储返回的主机和服务字符串,它们都需要由调用者预先分配存储空间。如果不想返回主机或服务字符串,可将 hostlen 或 servlen 参数指定为 0。
flags 参数可以用于改变 getnameinfo 的操作,可指定的值及其说明如下图所示。
在此需要注意的是,当调用者知道要处理的是数据报套接字时,应设置 NI_DGRAM 标志,因为套接字地址结构中仅给出了 IP 地址和端口号,getnameinfo 无法就此确定所用的协议。
上一篇: Unix 5 种 IO 模型概述
下一篇: JAVA实现FTP 文件上传和下载