当我想连接到服务器(在本地运行)并且不知道应用程序使用的是ipv4,ipv6还是两者一起使用时,我应该两次调用getaddrinfo()
,一次使用AF_INET
,一次使用AF_INET6
,并尝试所有返回的地址?
某些情况:
getaddrinfo()
提供了一种与ipv4 / ipv6无关的主机名解析的方法。在网上,我发现了一些准则,指出连接到服务器的通用算法是将getaddrinfo()
与AF_UNSPEC提示一起使用,并尝试列表中返回的地址。
但是,在我的设置中,getaddrinfo()
仅 在我使用AF_UNSPEC
时返回ipv6条目,主机为"localhost"
。
另一方面,如果我明确要求提供IPv4,则getaddrinfo()
确实会返回一个IPv4地址。
此示例使用getaddrinfo()
调用AF_UNSPEC
一次,并使用AF_INET
调用一次,并遍历返回的列表并打印条目的地址族:
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <string.h>
const char* family_to_string(int family) {
switch (family) {
case AF_INET:
return "AF_INET";
case AF_INET6:
return "AF_INET6";
case AF_UNSPEC:
return "AF_UNSPEC";
default:
return "UNKNOWN";
}
}
int main(void) {
struct addrinfo hints;
struct addrinfo *res, *it;
static const char* host = "localhost";
static const char* port = "42420";
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = 0;
hints.ai_family = AF_UNSPEC;
printf("getaddrinfo(.. %s ..):\n", family_to_string(hints.ai_family));
int ret = getaddrinfo(host, port, &hints, &res);
if (ret != 0) {
return 1;
}
for (it = res; it != NULL; it = it->ai_next) {
printf("entry for %s\n", family_to_string(it->ai_family));
}
printf("\n");
freeaddrinfo(res);
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = 0;
hints.ai_family = AF_INET;
printf("getaddrinfo(.. %s ..):\n", family_to_string(hints.ai_family));
ret = getaddrinfo(host, port, &hints, &res);
if (ret != 0) {
return 1;
}
for (it = res; it != NULL; it = it->ai_next) {
printf("entry for %s\n", family_to_string(it->ai_family));
}
printf("\n");
freeaddrinfo(res);
return 0;
}
收到此输出后,我很惊讶:
getaddrinfo(.. AF_UNSPEC ..):
entry for AF_INET6
getaddrinfo(.. AF_INET ..):
entry for AF_INET
在深入研究getaddrinfo()
的行为之后,似乎首先检查了/etc/hosts
中的条目,如果找到匹配的条目,则仅返回这些条目,而不尝试解析主机名不同。
由于我的/etc/hosts
文件仅包含localhost
的ipv6条目,因此仅返回AF_UNSPEC
的ipv6条目。对于AF_INET
,该条目不符合条件,并且localhost
已正确解析为127.0.0.1
。
答案 0 :(得分:2)
这确实是一个有趣的问题:
这种情况由glibc的nss-files
模块专门处理;如果发出了对地址族为AF_INET
的localhost的请求,并且解析了/ etc / hosts的v6 localhost条目,则将其隐式转换为地址为127.0.0.1
的v4 localhost条目。
请参见nss/nss_files/files-hosts.c
(在第70行附近)进行此转换的地方:
else if (IN6_IS_ADDR_LOOPBACK (entdata->host_addr))
{
in_addr_t localhost = htonl (INADDR_LOOPBACK);
memcpy (entdata->host_addr, &localhost, sizeof (localhost));
}
在请求AF_UNSPEC
时不采用此分支,因此只有在/etc/hosts
中有明确的条目时,您才可以解析v4本地主机地址。