Ohhnews

分类导航

$ cd ..
Baeldung原文

在Java中实现IPv4到IPv6地址的转换

#java#ipv4#ipv6#网络编程#协议转换

[LOADING...]

1. 概述

互联网协议第 6 版(IPv6)在 RFC 2460 中定义,是最新一代的互联网协议。它取代了地址空间有限的 IPv4。然而,IPv6 无法直接与 IPv4 互操作。因此,使用不同协议的设备经常会遇到兼容性问题。

为了解决这个问题,现代系统使用了诸如双栈(dual-stack)、隧道(tunneling)和转换(translation)等技术。这些方法使系统能够在支持 IPv6 的同时继续使用 IPv4。然而,这些技术主要工作在网络层(第 3 层)

在许多情况下,开发人员需要一种将 IPv4 地址表示为 IPv6 兼容格式的方法。因此,他们编写代码来实现两种格式之间的数据映射。

在本教程中,我们将了解如何使用 Java 将 IPv4 地址转换为 IPv6。

2. 转换意味着什么?

实际上,不存在从 IPv4 到 IPv6 的直接转换。这两种协议在结构和设计上各不相同。

相反,一种常见的方法是将 IPv4 嵌入到 IPv6 中。这种格式称为 IPv4 映射的 IPv6 地址(IPv4-mapped IPv6 address)。

此外,还有其他表示形式。但并非所有形式在今天都是标准的。例如,IPv4 兼容的 IPv6 地址(IPv4-compatible IPv6 addresses)已被弃用。同样,NAT646to4 充当的是过渡方法,而非真正的转换。

下面让我们看看如何在 Java 中实现上述技术。

3. Java 实现

本质上,Java 提供了内置类来处理 IP 地址。InetAddress 类同时支持 IPv4 和 IPv6。

3.1. 将 IPv4 转换为 IPv6 映射地址

让我们看看映射地址的情况。

为此,我们使用标准格式将 IPv4 地址转换为 IPv6 格式:

$ plaintext
::ffff:w.x.y.z

在上述情况下,w.x.y.z 是目标 IPv4 地址:

$ java
String toIpv4MappedIpv6(String ipv4Address) throws UnknownHostException {
    validateIpv4(ipv4Address);
    return "::ffff:" + ipv4Address;
}

此方法只是添加了 ::ffff: 前缀。结果,我们得到了一个有效的 IPv4 映射的 IPv6 地址。

3.2. IPv4 兼容的 IPv6 地址

值得注意的是,IPv4 兼容的 IPv6 地址格式已不再使用。不过,了解旧系统仍有帮助。

在十六进制表示法中,这种类型的地址将所有前导位设置为零

让我们再次以 IPv4 地址 w.x.y.z 为例:

$ plaintext
0000:0000:0000:0000:0000:0000:WWXX:YYZZ

该地址也可以通过省略所有前导零来压缩:

$ plaintext
::w.x.y.z 

此外,我们来看看它的实现:

$ java
String toIpv4CompatibleIpv6(String ipv4Address) throws UnknownHostException {
    validateIpv4(ipv4Address);
    return "::" + ipv4Address;
}

上述方法获取一个 IPv4 地址并返回一个 IPv6 地址。

因此,IPv4 地址 192.0.2.33 将返回 IPv4 兼容的 IPv6 地址 ::192.0.2.33

3.3. NAT64 实现

NAT64 风格的转换地址通常使用 64:ff9b::/96 前缀,后面紧跟目标 IPv4 地址。

例如,形式为 w.x.y.z 的 IP 地址将获得 NAT64 ID 64:ff9b::w.x.y.z

实现代码与之前的方法类似:

$ java
String toNat64Ipv6(String ipv4Address) throws UnknownHostException {
    validateIpv4(ipv4Address);
    return "64:ff9b::" + ipv4Address;
}

因此,如果我们有一个 IPv4 地址 192.0.2.33,则生成的地址应为 64:ff9b::192.0.2.33

3.4. 6to4 地址(隧道技术)

6to4 是一种过渡方法。它将 IPv6 数据包封装在 IPv4 数据包中。此外,出于此目的,互联网号码分配局(IANA)分配了 2002::/16 块。

例如,IPv4 地址 192.0.2.4 的十六进制等效值为 C000:0204在这种情况下,6to4 前缀变为 2002:C000:0204::/48

$ java
16 位(基数)+ 32 位(IPv4= 48

因此,前缀 2002::/16 后面跟着公共 IPv4 地址的 32 位。

让我们看看它的 Java 实现:

$ java
String toSixToFourIpv6(String ipv4Address) throws UnknownHostException {
    byte[] bytes = validateAndGetBytes(ipv4Address);
    return String.format("2002:%02x%02x:%02x%02x::",
      bytes[0] & 0xff,
      bytes[1] & 0xff,
      bytes[2] & 0xff,
      bytes[3] & 0xff);
}

上述方法将 IPv4 的每个字节转换为其十六进制表示形式。

最后,结果被附加到 2002::/16 前缀后面。

4. JUnit 测试

为了验证类方法,我们可以编写一个简单的 JUnit 测试:

$ java
class Ipv4ToIpv6ConverterUnitTest {
    private final Ipv4ToIpv6Converter converter = new Ipv4ToIpv6Converter();
    @Test
    void whenValidIpv4_thenReturnMappedIpv6() throws Exception {
        String result = converter.toIpv4MappedIpv6("192.168.1.1");
        assertEquals("::ffff:192.168.1.1", result);
    }
}

通过这种方式,我们可以检查输出是否与预期值匹配。同样,我们也可以为其他方法编写测试。

5. 使用场景

IPv4 映射的 IPv6 地址有助于两种协议在双栈系统中协同工作。

如果用户没有双栈支持,也可以使用隧道机制,如 6to4

同样,我们还有像 NAT64 这样的转换器,它们也有助于 IPv4 设备和 IPv6 设备之间的互操作。

转换的总体思路是使不同的协议能够同步工作。

6. 结论

在本文中,我们讨论了使用 Java 进行 IPv4 和 IPv6 之间的转换。由于两种 IP 协议在结构和语义上存在差异,我们无法真正将 IPv4 转换为 IPv6。

因此,上述方法并不能将 IPv4 主机变成原生的 IPv6 主机。相反,它们为系统协同工作提供了一种方式。Java 通过 InetAddress 类和字节处理简化了这一过程。

最终,应用程序可以高效地支持这两种地址格式。

本文的完整代码可在 GitHub 上获取。