IPv6的奇葩事

在双栈操做系统上,IPV6的套接字能够访问IPV4与IPV6的协议栈。因此只用建立一个IPV6 Socket,就能够接受来自IPv4和IPv6的链接。
IPv6的奇葩事IPv6的奇葩事
接受的IPv4的链接,会作IPv4到IPv6的地址转换(IPv4-mapped),以适应IPv6的数据结构。html

有时咱们用netstat看到服务只监听在IPv6的通配地址上,却能够接受IPv4的链接就是这样道理。(见参考1)java

禁用ipv6的Socket接受IPv4的链接的方法,对于C,C++等程序,能够经过setsocketopt选项IPV6_V6ONLY来完成,这样ipv6的Socket就再也不接受IPv4的链接。linux

然而对于Java程序,目前没法作到。只能经过更改linux配置,来改变系统层面Socket的默认行为:
例如: echo “1” > /proc/sys/net/ipv6/bindv6only
在JDK6下这种方式是有效的,绑定通配符,IP4再没法链接进来,但在JDK7下,好像这个bindv6only失效了。并且,
bind(new InetSocketAddress(InetAddress.getByName("192.168.9.50"), 8080))在JDK6
上报错: invalid argument, 在JDK7上正常,绑定地址是IPv4-mapped后的地址。可能的改变来自:https://bugs.java.com/view_bug.do?bug_id=6882910
OpenJDK代码段(DualStackPlainSocketImpl.c):
IPv6的奇葩事编程

IPV6_V6ONLY已固定设置为0.
为了知足BUG中提到的需求,JDK把这个弄的不灵活了。windows

附:Linux C编程代码段:数据结构

bzero(&servaddr, sizeof(servaddr));  
    servaddr.sin6_family = AF_INET6;  
    //若是绑定地址为locallink地址,则下面这句是必须的。
    servaddr.sin6_scope_id= if_nametoindex("eth0");
    inet_pton( AF_INET6,"2001::1", &servaddr.sin6_addr);  
    servaddr.sin6_port = htons(10004); 
    int no = 0;     
    setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (void *)&no, sizeof(no));
    bind(linenerfd, (struct sockaddr *) &servaddr, sizeof(struct sockaddr_in6)

Java程序段:
ServerSocket ss = new ServerSocket();<br/>ss.bind(new InetSocketAddress(InetAddress.getByName("2001::1"), 8080))oracle

与IPv4没什么差别。

对于ipv4的通配符0.0.0.0在ipv6环境下也是可用的:
linux C代码中虽然inet_pton( AF_INET6,"0.0.0.0", &servaddr.sin6_addr)会返回0,
但最后仍是bind到了通配符上。inet_pton对于非通配的ipv4的地址也返回0,
最终也绑定到了通配符上。因此说0.0.0.0成功的缘由来自一个错误的巧合。app

Java上bind(new InetSocketAddress(InetAddress.getByName("0.0.0.0"), 8080))
 效果与C编程结果一致

参考:
https://www.cnblogs.com/wlzjdm/p/8684202.html
https://docs.microsoft.com/zh-cn/windows/desktop/WinSock/dual-stack-sockets
https://docs.oracle.com/javase/8/docs/technotes/guides/net/ipv6_guide/index.html
https://files.cnblogs.com/files/Snowfun/IPv6supportinJava.pdfsocket

相关文章
相关标签/搜索