我正在尝试编写一个同时侦听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首先返回AF_INET,然后返回AF_INET6 --而根据规范,客户机的getaddrinfo首先返回AF_INET6。在我幼稚的方法中,服务器选择IPv4,客户端选择IPv6,连接失败。
我试图通过设置hints.ai_family = AF_INET6来解决这个问题,但在IPv6不可用的系统上失败了。
我看到了两个显而易见的解决方案:
a)尝试先请求IPv6,如果请求失败,则返回到IPv4,或者
b)遍历getaddrinfo的结果,查找IPv6,如果不存在,则选择第一个条目
但我不太喜欢其中任何一个;)我觉得应该有一种方法来说服getaddrinfo去做正确的事情,或者用一种不同的方法来实现我的目标。
发布于 2010-08-24 08:21:07
getaddrinfo()返回的地址顺序未指定,因此您必须准备好处理这两种情况。这可能意味着遍历列表,跟踪“目前为止最好的地址”。
或者,您可以尝试对getaddrinfo()返回的所有地址执行bind()和listen()操作。这可能是最好的选择,因为某些OSes不接受到侦听0::0的IPv6套接字的IPv4连接。
发布于 2014-07-10 18:49:44
您的代码应该按照您所描述的方式工作。不幸的是,正如launchpad bug #673708中描述的那样,glibc中存在一个bug,这导致它首先选择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_V6ONLY套接字选项以同时支持IPv6和IPv4。https://stackoverflow.com/questions/3552625
复制相似问题