Java实现 IPv6 与 long 相互转换

3 篇文章 1 订阅
2 篇文章 0 订阅

缘起

之前写了一篇《ip 地址与 int 整数的相互转换》,公众号 Java面试那些事转发了,有读者评论问到 IPv6 的转换方法,于是抽时间也自己实现了一下。
公众号文章评论提问

IPv6 定义

IPv6是英文“Internet Protocol Version 6”(互联网协议第6版)的缩写,是互联网工程任务组(IETF)设计的用于替代IPv4的下一代IP协议,其地址数量号称可以为全世界的每一粒沙子编上一个地址。IPv6的地址长度为128位,它有3种表示方法,分别是冒分十六进制表示法、0位压缩表示法、内嵌IPv4地址表示法。

分析

首先,IPv6 的地址长度为 128 位,而 Java 中没有 128 位的原生数字,int 为 32 位,long 是 64 位,因此若要将 IPv6 地址直接转为 long, 则会丢掉一半的信息,这肯定是不能接受的。

因此,解决方式有两种思路。第一,使用 BigInteger;第二,将 IPv6 地址的 128 位拆分为两个 64 位的地址,即可存到两个 long 整数组成的数组中。本文采用后者,即将 IPv6 地址转换为 long 数组。

代码实现

另外,为简便起见,我们只考虑冒分十六进制表示法的情况,即完整的ip地址,如 0:0:0:0:0:0:0:0,0位压缩表示法和内嵌 IPv4 地址表示法暂不考虑。

将 IPv6 地址转为 long 数组

/**
* 将 IPv6 地址转为 long 数组,只支持冒分十六进制表示法
 */
public static long[] ip2Longs(String ipString) {
 	if (ipString == null || ipString.isEmpty()) {
        throw new IllegalArgumentException("ipString cannot be null.");
    }
    String[] ipSlices = ipString.split(":");
    if (ipSlices.length != 8) {
        throw new IllegalArgumentException(ipString + " is not an ipv6 address.");
    }
    long[] ipv6 = new long[2];
    for (int i = 0; i < 8; i++) {
        String slice = ipSlices[i];
        // 以 16 进制解析
        long num = Long.parseLong(slice, 16);
        // 每组 16 位
        long right = num << (16 * i);
        // 每个 long 保存四组,i >> 2 = i / 4
        ipv6[i >> 2] |= right;
    }
    return ipv6;
}

将 long 数组转为 IPv6 地址

  /**
     * 将 long 数组转为冒分十六进制表示法的 IPv6 地址
     */
    public static String longs2Ip(long[] numbers) {
        if (numbers == null || numbers.length != 2) {
            throw new IllegalArgumentException(Arrays.toString(numbers) + " is not an IPv6 address.");
        }
        StringBuilder sb = new StringBuilder(32);
        for (long numSlice : numbers) {
            // 每个 long 保存四组
            for (int j = 0; j < 4; j++) {
                // 取最后 16 位
                long current = numSlice & 0xFFFF;
                sb.append(Long.toString(current, 16)).append(":");
                // 右移 16 位,即去除掉已经处理过的 16 位
                numSlice >>= 16;
            }
        }
        // 去掉最后的 :
        return sb.substring(0, sb.length() - 1);
    }

测试一下

public static void main(String[] args) {
    String[] ips4Test = new String[]{"FFFF:FFFF:7654:FEDA:1245:BA98:3210:4562",
            "FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF", "7654:0:FFFF:7654:562:222:7622:0", "0:0:0:0:0:0:0:0"};
    for (String testCase : ips4Test) {
        test(testCase);
    }
}

private static void test(String testCase) {
    long[] ipv6 = ip2Longs(testCase);
    String ipString = longs2Ip(ipv6);
    boolean eq = testCase.equalsIgnoreCase(ipString);
    System.out.println("本次测试 ipv6 地址: " + testCase + ", 转为 long 数组: " + Arrays.toString(ipv6)
            + ", 再转回 ipv6 字符串: " + ipString + ", 是否与原字符串相等: " + eq);
    if (!eq) {
        throw new IllegalStateException("本次测试未通过!testCase: " + testCase);
    }
}

输出结果:

本次测试 ipv6 地址: FFFF:FFFF:7654:FEDA:1245:BA98:3210:4562, 转为 long 数组: [-82623535708635137, 4999613583766065733], 再转回 ipv6 字符串: ffff:ffff:7654:feda:1245:ba98:3210:4562, 是否与原字符串相等: true
本次测试 ipv6 地址: FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF, 转为 long 数组: [-1, -1], 再转回 ipv6 字符串: ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff, 是否与原字符串相等: true
本次测试 ipv6 地址: 7654:0:FFFF:7654:562:222:7622:0, 转为 long 数组: [8526721465200965204, 129888436749666], 再转回 ipv6 字符串: 7654:0:ffff:7654:562:222:7622:0, 是否与原字符串相等: true
本次测试 ipv6 地址: 0:0:0:0:0:0:0:0, 转为 long 数组: [0, 0], 再转回 ipv6 字符串: 0:0:0:0:0:0:0:0, 是否与原字符串相等: true

转载请注明出处:https://blog.csdn.net/dadiyang/article/details/88285937

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值