getaddrinfo函数:
该函数可通过知己名获取IP地址(内部使用的是gethostbyname
函数),也能通过服务名获得端口号(内部使用的是getservbyname
函数)。其是否可重入取决于其内部的gethostbyname
和getservbyname
函数是否是它们的可重入版本,该函数的定义如下:
int getaddrinfo(const char *hostname, const char *service,const struct addrinfo *hints,struct addrinfo **res);
hostname参数接受主机名也可以接受字符串表示的IP地址。
service参数接受服务名也可以接受端口号。
hints参数是应用程序给getaddrinfo的一个提示,以对getaddrinfo的输出进行更精准的控制,其可被设置为NULL,表示允许getaddrinfo反馈任何可用的结果。
res参数指向一个链表,该链表用于存储getaddrinfo反馈的结果,其反馈的每一条结果都是addinfo结构体类型的对象.
结构体addrinfo定义如下:
struct addrinfo {
int ai_flags; /*见下面表格*/
int ai_family; /*地址族*/
int ai_socktype; /*服务类型,SOCK_STREAM或SOCK_DGRAM*/
int ai_protocol; /*服务类型所对应的网络协议,其含义和socket系统调用的第三个参数相同*/
socklen_t ai_addrlen; /*sock地址ai_addr的长度*/
struct sockaddr *ai_addr; /*指向socket地址*/
char *ai_canonname; /*主机别名*/
struct addrinfo *ai_next; /*指向下一个节点*/
};
其中参数ai_protocol通常设置为0,表示服务类型所对应的默认协议,ai_flags成员可以取下面表格中的标志的按位或。
选项 | 含义 |
---|---|
AI_PASSIVE | 在hints参数中设置,表示调用者是否会将取得socket地址用于被动打开。服务器通常需要设置它,表示接受任何本地socket地址上的服务请求。客户端程序不能设置它 |
AI_CANONNAME | 在hints参数中设置,告诉getaddrinfo函数返回主机的别名 |
AI_NUMERICHOST | 在hints参数中设置,表示hostname必须是用字符串表示的ip地址,从而避免DNS查询 |
AI_NUMERICSERV | 在hints参数中设置,强调service参数使用十进制端口号的字符串形式,不能是服务名 |
AI_V4MAPPED | 在hints参数中设置。如果ai_family被设置为AF_INET6,那么当没有满足条件的IPV6地址找到时,将IPV4地址映射为IPv6地址 |
AI_ALL | 必须和AI_V4MAPPED同时使用,否则将被忽略。表示同时返回符合条件的IPV6地址以及由IPv4地址映射得到的IPV6地址 |
AI_ADDRCONFIG | 仅当至少配置有一个IPV4(除了回路地址)时,才返回IPv4地址信息;同样,仅当至少配置有一个IPV6地址(除了回路地址)时,才返回IPv6地址信息。它和AI_v4MAPPED是互斥的 |
当我们使用hints参数时,可以设置ai_family,ai_flags,ai_socktype和ai_protocol四个字段,其他字段则必须被设置为NULL。
由于getaddrinfo
函数第四个参数返回的是链表,它指向的空间由函数隐式地分配堆内存,所以我们要配套下面的函数来释放这块内存:
void freeaddrinfo(struct addrinfo *res);
getnameinfo函数:
该函数能通过socket地址同时获得以字符串表示的主机名(内部使用的是gethostbyaddr
函数)和服务名(内部使用的是getservbyport
函数)。他是否可重入取决于内部调用的那两个函数是否为可重入的版本。函数原型如下:
int getnameinfo(const struct sockaddr *addr, socklen_t addrlen,
char *host, socklen_t hostlen,
char *serv, socklen_t servlen, int flags);
getnameinfo函数将返回主机名存储在host参数指向的缓存中,将服务名存储在serv参数指向的缓存中,hostlen和servlen参数分别指定这两块缓存的长度。flags参数控制函数行为,它可以接受下表的选项。
选项 | 含义 |
---|---|
NI_NAMEEREQD | 如果通过socket地址不能获得主机名,则返回错误 |
NI_DGRAM | 返回数据报服务。大部分同时支持流和数据报的服务使用相同的端口号来提供这两种服务。但端口512~514是例外。比如TCP的514端口提供的是sell登录服务,而UDP的514端口提供的是syslog服务(参见/etc/services文件) |
NI_NUMERICHOST | 返回字符串表示的IP地址而不是主机名 |
NI_NUMERICSERV | 返回字符串表示的十进制端口号,而不是主机名 |
NI_NOFQDN | 仅返回主机域名的第一部分。比如对主机名nebula.testing.com,getnameinfo只将nebula写入host缓存 |
两个函数的返回值:
成功返回0,失败返回错误码,可能的错误码如下表所示:
选项 | 含义 |
---|---|
EAI_AGAIN | 调用临时失败,提示应用程序过后再试 |
EAI_BADFLAGS | 非法的ai_flags值 |
EAI_FAIL | 名称解析失败 |
EAI_FAMILY | 不支持的ai_family参数 |
EAI_MEMORY | 内存分配失败 |
EAI_NONAME | 非法的主机名或服务名 |
EAI_OVERFLOW | 用户提供的缓冲区溢出,仅发生在getnameinfo调用中 |
EAI_SERVICE | 没有支持的服务,比如用数据报服务类型来查找ssh服务。因为ssh服务只能用流服务 |
EAI_SOCKTYPE | 不支持的服务类型。如果hints.ai_socktype和hints.ai_protocol不一样,比如前者指定SOCK_STREAM,而后者使用的是IPROTO_TCP,则会触发这类错误 |
EAI_SYSTEM | 系统错误,错误值存储在errno中 |