在双栈操做系统上,IPV6的套接字能够访问IPV4与IPV6的协议栈。因此只用建立一个IPV6 Socket,就能够接受来自IPv4和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_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