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

相關文章
相關標籤/搜索