在雙棧操做系統上,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