关于ipv6:Java对IPv6的支持详解支持情况相关API演示代码等

32次阅读

共计 10418 个字符,预计需要花费 27 分钟才能阅读完成。

本文由朱益盛、杨晖、傅啸分享,来自 IBM Developer 社区,原题“应用 Java 开发兼容 IPv6 的网络应用程序”,本次收录时有改变。

1、引言

前几天,有个群友跟我探讨用 MobileIMSDK 写的 IM 服务端想反对 IPv6 的问题。因为众所周之的起因,IPv4 早就不够用,当初国内从国家层面都在大力推广 IPv6 的遍及,所以包含事业单位、国企在内,当初搞信息化倡议,都要思考 IPv6 的反对。

我突然感觉这个问题很难答复,因为对于一般的网络通信程序开发者来说,目前真正的 IPv6 的开发和测试环境并不容易失去,所以想要真正说分明 Java 对于 IPv6 地反对状况,只能借助系统的材料和网贴,可能并不残缺和筹备。

实践上,Java 对 IPv6 的反对对于程序员来说都是通明的,简直不须要代码层面的解决。但它到底是怎么反对的?反对到什么水平?对 JDK 版本有什么要求?对操作系统有什么要求?等等,我认为还是有必要具体钻研理解一下。

本文将用通俗易懂的文字,来解说 Java 对 IPv6 的反对现状,包含关的技术原理、能够应用的 API、以及一些能够运行的演示代码片段等,心愿能让你更直观的理解 Java 对于 IPv6 的反对状况。

浏览提醒:限于篇幅,本文假如你已理解 IPv6 技术是什么,如您对它无所不知,倡议先浏览文言式入门文章:《一文读懂什么是 IPv6》。

(本文同步公布于:http://www.52im.net/thread-3236-1-1.html)

2、举荐材料

《IPv6 技术详解:基本概念、利用现状、技术实际(上篇)》
《IPv6 技术详解:基本概念、利用现状、技术实际(下篇)》

3、技术背景

目前咱们应用的是第二代互联网 IPv4 技术,它的最大问题是网络地址资源无限,从实践上讲,能够编址 1600 万个网络、40 亿台主机。但采纳 A、B、C 三类编址形式后,可用的网络地址和主机地址的数目大打折扣,以至目前的 IP 地址近乎枯竭。网络地址有余,重大地制约了寰球互联网的利用和倒退。

▲ 本图援用自《网络编程懒人入门(十一):一文读懂什么是 IPv6》

一方面是地址资源数量的限度,另一方面是随着电子技术及网络技术的倒退,计算机网络将进入人们的日常生活,可能身边的每一样货色都须要连入寰球因特网。在这种网络空间匮乏的环境下,IPv6 应运而生。它的产生岂但解决了网络地址资源数量的问题,同时也为除电脑外的设施连入互联网在数量限度上扫清了阻碍。

如果说 IPv4 实现的只是人机对话,那么 IPv6 则扩大到任意事物之间的对话,它不仅能够为人类服务,还将服务于泛滥硬件设施,如家用电器、传感器、近程照相机、汽车等,它将是无时不在,无处不在的深刻社会每个角落的真正的宽带网,它所带来的经济效益也将十分微小。

当然,IPv6 并非美中不足、一劳永逸,不可能解决所有问题。IPv6 只能在倒退中不断完善,也不可能在一夜之间产生,过渡须要工夫和老本,但从长远看,IPv6 有利于互联网的继续和短暂倒退。目前,国内互联网组织曾经决定成立两个专门工作组,制订相应的国际标准。

4、Java 对 IPv6 的反对

随着 IPv6 越来越受到业界的器重,Java 从 1.4 版开始反对 Linux 和 Solaris 平台上的 IPv6。1.5 版起又退出了 Windows 平台上的反对。

绝对于 C++,Java 很好得封装了 IPv4 和 IPv6 的变动局部,遗留代码都能够原生反对 IPv6,而不必随底层具体实现的变动而变动。

那么 Java 是如何来反对 IPv6 的呢?

Java 网络栈会优先查看底层零碎是否反对 IPv6,以及采纳的何种 IP 栈零碎。如果是双栈零碎,那它间接创立一个 IPv6 套接字(如图 1)。

图 1 – 双栈构造:

对于分隔栈零碎,Java 则创立 IPv4/v6 两个套接字(如图 2):

  • 1)如果是 TCP 客户端程序:一旦其中某个套接字连贯胜利,另一个套接字就会被敞开,这个套接字连贯应用的 IP 协定类型也就此被固定下来;
  • 2)如果是 TCP 服务器端程序:因为无奈预期客户端应用的 IP 协定,所以 IPv4/v6 两个套接字会被始终保留;
  • 3)对于 UDP 应用程序:无论是客户端还是服务器端程序,两个套接字都会保留来实现通信。

图 2 – 分隔栈构造:

5、如何验证 IPv6 地址

5.1 IPv6 地址示意

从 IPv4 到 IPv6 最显著的变动就是网络地址的长度,IPv6 地址为 128 位长度,个别采纳 32 个十六进制数,但通常写做 8 组每组 4 个十六进制的模式。

IPv6 地址组成如下图所示:

▲ 本图援用自《网络编程懒人入门(十一):一文读懂什么是 IPv6》

例如:

  • 1)2001:0db8:85a3:08d3:1319:8a2e:0370:7344 是一个非法的 IPv6 地址。如果四个数字都是零,则能够被省略;
  • 2)2001:0db8:85a3:0000:1319:8a2e:0370:7344 等同于 2001:0db8:85a3::1319:8a2e:0370:7344。

听从这些规定,如果因为省略而呈现了两个以上的冒号的话,能够压缩为一个,但这种零压缩在地址中只能呈现一次。

因而:

2001:0DB8:0000:0000:0000:0000:1428:57ab
2001:0DB8:0000:0000:0000::1428:57ab
2001:0DB8:0:0:0:0:1428:57ab
2001:0DB8:0::0:1428:57ab
2001:0DB8::1428:57ab

都是非法的地址,并且他们是等价的。但 _2001::25de::cade_ 是非法的(因为这样会使得搞不清楚每个压缩中有几个全零的分组)。同时前导的零能够省略,因而:_2001:0DB8:02de::0e13_ 等于 _2001: DB8:2de::e13_。

5.2 IPv6 地址校验

IPv4 地址能够很容易的转化为 IPv6 格局。

举例来说:如果 IPv4 的一个地址为 _135.75.43.52_(十六进制为 _0x874B2B34_),它能够被转化为 _0000:0000:0000:0000:0000:0000:874B:2B34_ 或者::874B:2B34。同时,还能够应用混合符号(IPv4- compatible address),则地址能够为:_:135.75.43.52_。

在 IPv6 的环境下开发 Java 利用,或者移植已有的 IPv4 环境下开发的 Java 利用到 IPv6 环境中来,对于 IPv6 网络地址的验证是必须的步骤,尤其是对那些提供了 UI(用户接口)的 Java 利用。

所幸的是:从 Java 1.5 开始,Java 就减少了对 IPv6 网络地址校验的反对。程序员能够通过简略地调用办法 _sun.net.util.IPAddressUtil.isIPv6LiteralAddress()_ 来验证一个 String 类型的输出是否是一个非法的 IPv6 网络地址。

为了更深刻一步地理解 IPv6 的网络地址标准,及其验证算法,笔者参阅了一些资料,包含上文所述的办法 sun.net.util.IPAddressUtil.isIPv6LiteralAddress() 的源代码,以及目前网络上流传的一些 IPv6 网络地址的正则表达式,发现:

  • 1)因为 IPv6 协定所容许的网络地址格局较多,标准较宽松(例如零压缩地址,IPv4 映射地址等),所以导致了 IPv6 网络地址的格局变化很大;
  • 2)Java 对于 IPv6 网络地址的验证是通过对输出字符的循环匹配做到的,并没有采取正则表达式的做法。其匹配过程中还依赖于其它的 Java 办法;
  • 3)目前网络上流传的 IPv6 网络地址验证的正则表达式通常都只能涵盖局部地址格局,而且表达式简短难读,十分不易于了解。

基于通用性思考,以及为了使验证办法尽量简略易读,笔者尝试将 IPv6 网络地址的格局简略分类当前,应用多个正则表达式进行验证。

这种做法兼顾了通用性(基于正则表达式,所以不便用各种不同的编程语言进行实现),以及易读性(每个独立的正则表达式绝对简短);并且依据测试,反对目前所有的 IPv6 网络地址格局类型,尚未发现例外。

以下是笔者用 Java 编写的对于 IPv6 网络地址的验证办法。此算法可被简略地用其它编程语言仿照重写。

演示代码 1 – 验证地址:

//IPv6 address validator matches these IPv6 formats
//::ffff:21:7.8.9.221 | 2001:0db8:85a3:08d3:1319:8a2e:0370:7344
//| ::8a2e:0:0370:7344 | 2001:0db8:85a3:08d3:1319:8a2e:100.22.44.55
//| 2001:0db8::8a2e:100.22.44.55 | ::100.22.44.55 | ffff::
//And such addresses are invalid
//::8a2e:0:0370:7344.4 | 2001:idb8::111:7.8.9.111 | 2001::100.a2.44.55
//| :2001::100.22.44.55
public static boolean isIPV6Format(String ip) {
    ip = ip.trim();
    //in many cases such as URLs, IPv6 addresses are wrapped by []
    if(ip.substring(0, 1).equals(“[“) && ip.substring(ip.length()-1).equals(“]”))
        ip = ip.substring(1, ip.length()-1);
        return(1< Pattern.compile(“:”).split(ip).length)
        //a valid IPv6 address should contains no less than 1,
        //and no more than 7 “:”as separators
            && (Pattern.compile(“:”).split(ip).length <= 8)
        //the address can be compressed, but “::”can appear only once
            && (Pattern.compile(“::”).split(ip).length <= 2)
        //if a compressed address
            && (Pattern.compile(“::”).split(ip).length == 2)
            //if starts with “::”– leading zeros are compressed
            ? (((ip.substring(0, 2).equals(“::”))
            ? Pattern.matches(“^::([da-f]{1,4}(:)){0,4}(([da-f]{1,4}(:)[da-f]{1,4})
        |([da-f]{1,4})|((d{1,3}.){3}d{1,3}))”, ip)
                : Pattern.matches(“^([da-f]{1,4}(:|::)){1,5}
        (([da-f]{1,4}(:|::)[da-f]{1,4})|([da-f]{1,4})
        |((d{1,3}.){3}d{1,3}))”, ip)))
        //if ends with “::” – ending zeros are compressed
                : ((ip.substring(ip.length()-2).equals(“::”))
                ? Pattern.matches(“^([da-f]{1,4}(:|::)){1,7}”, ip)
                : Pattern.matches(“^([da-f]{1,4}:){6}(([da-f]{1,4}
        :[da-f]{1,4})|((d{1,3}.){3}d{1,3}))”, ip));
    }}

6、如何正规化 IPv6 地址

在网络程序开发中,常常应用 IP 地址来标识一个主机,例如记录终端用户的拜访记录等。因为 IPv6 具备有零压缩地址等多种示意模式,因而间接应用 IPv6 地址作为标示符,可能会带来一些问题。

为了防止这些问题,在应用 IPv6 地址之前,有必要将其正规化。

除了通过咱们熟知的正则表达式,笔者在开发过程中发现应用一个简略的 Java API 也能够达到雷同的成果。

演示代码 2 – 正规化地址:

InetAddress inetAddr = InetAddress.getByName(ipAddr);
ipAddr = inetAddr.getHostAddress();
System.out.println(ipAddr);

InetAddress.getByName(String) 办法承受的参数既能够是一个主机名,也能够是一个 IP 地址字符串。

咱们输出任一信息的非法 IPv6 地址,再通过 getHostAddress() 办法取出主机 IP 时,地址字符串 ipAddr 曾经被转换为残缺模式。

例如输出 2002:97b:e7aa::97b:e7aa,上述代码执行过后,零压缩局部将被还原,ipAddr 变为 2002:97b:e7aa:0:0:0:97b:e7aa。

7、如何获取本机 IPv6 地址

有时为了可能注册 listener,开发人员须要应用本机的 IPv6 地址,这一地址不能简略得通过 InetAddress.getLocalhost() 取得。因为这样有可能取得诸如 _0:0:0:0:0:0:0:1_ 这样的非凡地址。应用这样的地址,其余服务器将无奈把告诉发送到本机上,因而必须先进行过滤,选出的确可用的地址。以下代码实现了这一性能,思路是遍历网络接口的各个地址,直至找到符合要求的地址。

演示代码 3 – 获取本机 IP 地址:

public static String getLocalIPv6Address() throws IOException {
    InetAddress inetAddress = null;
    Enumeration<NetworkInterface> networkInterfaces = NetworkInterface
        .getNetworkInterfaces();
    outer:
    while(networkInterfaces.hasMoreElements()) {
        Enumeration<InetAddress> inetAds = networkInterfaces.nextElement()
        .getInetAddresses();
        while(inetAds.hasMoreElements()) {
            inetAddress = inetAds.nextElement();
            //Check if it’s ipv6 address and reserved address
            if(inetAddress instanceofInet6Address
                && !isReservedAddr(inetAddress)) {
                break outer;
            }
        }
    }
    String ipAddr = inetAddress.getHostAddress();
    // Filter network card No
    int index = ipAddr.indexOf(‘%’);
    if(index > 0) {
        ipAddr = ipAddr.substring(0, index);
    }
    return ipAddr;
}
/**
 * Check if it’s “local address” or “link local address” or “loopbackaddress”
 * @param ip address
 * @return result
 */
private static boolean isReservedAddr(InetAddress inetAddr) {
    if(inetAddr.isAnyLocalAddress() || inetAddr.isLinkLocalAddress()
        || inetAddr.isLoopbackAddress()) {
        return true;
    }
    return false;
}

为了反对 IPv6,Java 中减少了两个 InetAddress 的子类:Inet4Address 和 Inet6Address。

个别状况下这两个子类并不会被应用到,然而当咱们须要别离解决不同的 IP 协定时就十分有用,在这咱们依据 Inet6Address 来筛选地址。

_isReservedAddr()_ 办法过滤了本机非凡 IP 地址,包含”LocalAddress”,”LinkLocalAddress”和”LoopbackAddress”。读者可依据本人的须要批改过滤规范。

另一个须要留神的中央是:在 windows 平台上,获得的 IPv6 地址前面可能跟了一个百分号加数字。这里的数字是本机网络适配器的编号。这个后缀并不是 IPv6 规范地址的一部分,能够去除。

8、IPv4/IPv6 双环境下,网络的抉择和测试

咱们先看一下笔者所在的 IPv4/IPv6 开发测试环境及其配置办法。

笔者所处的 IPv4/IPv6 双环境是一个典型的”6to4”双栈网络,其中存在着一个 IPv6 到 IPv4 的映射机制,即任意一个 IPv6 地址 2002:92a:8f7aac:d 在路由时会被默认映射为 IPv4 地址 a.b.c.d,所以路由表只有一套。

在此环境内,IPv4 地址与 IPv6 地址的一一对应是人工保障的。如果一台客户机应用不匹配的 IPv4 和 IPv6 双地址,或者同时应用 DHCPv4 和 DHCPv6(可能会导致 IPv4 地址和 IPv6 地址不匹配),会导致 IPv6 的路由寻址失败。

正因为如此,为了配置双地址环境,咱们个别应用 DHCPv4 来主动获取 IPv4 地址,而后人工配置绝对应的 IPv6 地址。

Windows 零碎:

1)Windows 2000 及以下:不反对 IPv6
2)Windows 2003 和 Windows XP:应用 Windows 自带的 netsh 命令行形式增加 IPv6 地址以及 DNS,例如:C:>netsh interface ipv6 add address“Local Area Connection”2002:92a:8f7a10:13:1:2 和 C:>netsh interface ipv6 add dns“Local Area Connection”2002:92a:8f7a10::250
3)Windows 2008 和 Windows Vista:既能够应用 Windows 网络属性页面进行配置,也能够应用相似 Windows 2003 和 Windows XP 的 netsh 命令行来配置

Linux 零碎(以下是 IPv6 的长期配置办法,即不批改配置文件,计算机重启后配置生效):

1)Redhat Linux:最简略的办法是应用 ifconfig 命令行增加 IPv6 地址,例如:ifconfig eth0 inet6 add 2002:92a:8f7a10:14:24:106/96;
2)SUSE Linux:同上。

从实际上讲:因为 Java 的面向对象个性,以及 java.net 包对于 IP 地址的良好封装,从而使得将 Java 利用从 IPv4 环境移植到 IPv4/IPv6 双环境,或者纯 IPv6 环境变得异样简略。通常咱们须要做的仅是查看代码并移除明码编写的 IPv4 地址,用主机名来代替则可。

除此以外:对于一些非凡的需要,Java 还提供了 InetAddress 的两个扩大类以供应用:Inet4Address 和 Inet6Address,其中封装了对于 IPv4 和 IPv6 的非凡属性和行为。

然而因为 Java 的多态个性,使得程序员个别只须要应用父类 InetAddress,Java 虚拟机能够依据所封装的 IP 地址类型的不同,在运行时抉择正确的行为逻辑。所以在少数状况下,程序员并不需要准确管制所应用的类型及其行为,所有交给 Java 虚拟机即可。

具体的新增类型及其新增办法,请具体参阅 Java 的 API 文档。

另外:在 IPv4/IPv6 双环境中,对于应用 Java 开发的网络应用,比拟值得注意的是以下两个 IPv6 相干的 Java 虚拟机零碎属性。

java.net.preferIPv4Stack=<true|false>
java.net.preferIPv6Addresses=<true|false>

preferIPv4Stack(默认 false)示意如果存在 IPv4 和 IPv6 双栈,Java 程序是否优先应用 IPv4 套接字。默认值是优先应用 IPv6 套接字,因为 IPv6 套接字能够与对应的 IPv4 或 IPv6 主机进行对话;相同如果优先应用 IPv4,则只不能与 IPv6 主机进行通信。

preferIPv6Addresses(默认 false)示意在查问本地或远端 IP 地址时,如果存在 IPv4 和 IPv6 双地址,Java 程序是否优先返回 IPv6 地址。Java 默认返回 IPv4 地址次要是为了向后兼容,以反对旧有的 IPv4 验证逻辑,以及旧有的仅反对 IPv4 地址的服务。

9、写在最初

本文对 IPv6 地址做了一些根本的介绍,着重介绍了如何应用 Java 开发兼容 IPv6 的网络应用程序,包含如何验证 IPv6 地址,如何正规化 IPv6 地址的示意,如何获取本机 IPv6 的地址,以及在 IPv4/IPv6 双地址环境下的网络抉择和测试。

同时作者联合在日常工作中应用的 Java 代码片段,心愿出现给读者一个全方位的、具备较强实用性的文本介绍,也心愿本文能给读者在当前应用 Java 开发 IPv6 兼容程序的过程中带来一些帮忙。

10、参考资料

[1] IPv6 地址技术架构

[2] IPv6 协定技术文档

[3] Networking IPv6 User Guide for JDK/JRE 5.0

附录:相干文章

《技术往事:扭转世界的 TCP/IP 协定(宝贵多图、手机慎点)》
《通俗易懂 - 深刻了解 TCP 协定(上):实践根底》
《通俗易懂 - 深刻了解 TCP 协定(下):RTT、滑动窗口、拥塞解决》
《计算机网络通信协定关系图(中文珍藏版)》
《P2P 技术详解 (一):NAT 详解——具体原理、P2P 简介》
《P2P 技术详解(二):P2P 中的 NAT 穿梭(打洞) 计划详解 (基本原理篇)》
《P2P 技术详解(三):P2P 中的 NAT 穿梭(打洞) 计划详解(进阶剖析篇)》
《P2P 技术详解(四):P2P 技术之 STUN、TURN、ICE 详解》
《通俗易懂:疾速了解 P2P 技术中的 NAT 穿透原理》
《高性能网络编程(一):单台服务器并发 TCP 连接数到底能够有多少》
《高性能网络编程(二):上一个 10 年,驰名的 C10K 并发连贯问题》
《高性能网络编程(三):下一个 10 年,是时候思考 C10M 并发问题了》
《高性能网络编程(四):从 C10K 到 C10M 高性能网络应用的实践摸索》
《高性能网络编程(五):一文读懂高性能网络编程中的 I / O 模型》
《高性能网络编程(六):一文读懂高性能网络编程中的线程模型》
《高性能网络编程(七):到底什么是高并发?一文即懂!》
《网络编程懒人入门(二):疾速了解网络通信协定(下篇)》
《网络编程懒人入门(三):疾速了解 TCP 协定一篇就够》
《网络编程懒人入门(四):疾速了解 TCP 和 UDP 的差别》
《网络编程懒人入门(五):疾速了解为什么说 UDP 有时比 TCP 更有劣势》
《网络编程懒人入门(六):史上最艰深的集线器、交换机、路由器性能原理入门》
《网络编程懒人入门(七):深入浅出,全面了解 HTTP 协定》
《网络编程懒人入门(八):手把手教你写基于 TCP 的 Socket 长连贯》
《网络编程懒人入门(九):艰深解说,有了 IP 地址,为何还要用 MAC 地址?》
《网络编程懒人入门(十):一泡尿的工夫,疾速读懂 QUIC 协定》
《网络编程懒人入门(十一):一文读懂什么是 IPv6》
《网络编程懒人入门(十二):疾速读懂 Http/ 3 协定,一篇就够!》
《脑残式网络编程入门(一):跟着动画来学 TCP 三次握手和四次挥手》
《脑残式网络编程入门(二):咱们在读写 Socket 时,到底在读写什么?》
《脑残式网络编程入门(三):HTTP 协定必知必会的一些常识》
《脑残式网络编程入门(四):疾速了解 HTTP/ 2 的服务器推送(Server Push)》
《脑残式网络编程入门(五):每天都在用的 Ping 命令,它到底是什么?》
《脑残式网络编程入门(六):什么是公网 IP 和内网 IP?NAT 转换又是什么鬼?》
《脑残式网络编程入门(七):面视必备,史上最艰深计算机网络分层详解》
《脑残式网络编程入门(八):你真的理解 127.0.0.1 和 0.0.0.0 的区别?》
《脑残式网络编程入门(九):面试必考,史上最艰深大小端字节序详解》
《可能会搞砸你的面试:你晓得一个 TCP 连贯上能发动多少个 HTTP 申请吗?》
《5G 时代曾经到来,TCP/IP 老矣,尚能饭否?》

本文已同步公布于“即时通讯技术圈”公众号。

▲ 本文在公众号上的链接是:点此进入,原文链接是:http://www.52im.net/thread-3236-1-1.html

正文完
 0