3

我正在尝试编写一个侦听 IPv6 和 IPv4 连接的服务器应用程序。完成此任务的正确方法似乎是侦听 IPv6 地址,该地址也将接受 IPv4 连接。

相关的代码是:

memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
getaddrinfo(NULL, MYPORT, &hints, &res);

(几乎从 Beej's Guide 复制粘贴)

问题是,至少在我的系统上,返回第一个和第二个getaddrinfo条目- 而客户端的返回首先,根据规范。用我天真的方法,服务器选择 IPv4,客户端选择 IPv6,连接失败。AF_INETAF_INET6getaddrinfoAF_INET6

我试图通过设置来解决这个问题hints.ai_family = AF_INET6,但在 IPv6 不可用的系统上失败了。

我看到了两个明显的解决方案:
a)尝试首先请求 IPv6,如果失败则回退到 IPv4,或者
b)遍历结果getaddrinfo,查找 IPv6,如果不存在,则选择第一个条目
,但我不喜欢要么一个太多;)我觉得应该有一种方法来说服getaddrinfo做正确的事情,或者可能是一种不同的方法来实现我的目标。

4

2 回答 2

2

返回地址的顺序getaddrinfo()未指定,因此您必须准备好处理任何一种情况。这可能意味着遍历列表,跟踪“迄今为止看到的最佳地址”。

bind()或者,您可以尝试listen()所有getaddrinfo(). 这可能是最好的选择,因为某些操作系统接受 IPv4 连接到正在侦听的 IPv6 套接字0::0

于 2010-08-24T00:21:06.793 回答
1

您的代码应该按照您描述的方式工作。不幸的是,如启动板错误 #673708中所述,glibc 中有一个错误,导致它首先选择 IPv4。

电脑配置解决方案

有一个解决方法可以在您运行服务器程序的每台 Linux 计算机上完成:编辑/etc/gai.conf,启用所有默认规则(取消注释它们):

label ::1/128       0
label ::/0          1
label 2002::/16     2
label ::/96         3
label ::ffff:0:0/96 4
label fec0::/10     5
label fc00::/7      6
label 2001:0::/32   7

然后加:

label ::ffff:7f00:1/128 8

然后,如果支持,您的代码应该打开 IPv6,并且还将接受 IPv4 连接。

代码解决方案

如果上述方法不实用(只有在您愿意更改运行的每台计算机上的配置时才实用),然后修改您的代码以更喜欢 IPv6。例如我已经这样做了:

  • getaddrinfo()结果进行三遍。
  • 第一关,首选IPv6。尝试清除IPV6_V6ONLY套接字选项以支持 IPv6 和 IPv4。
  • 第二遍,更喜欢 IPv4。
  • 第三遍,拿走任何可用的东西。
于 2014-07-10T10:49:43.997 回答