IPv6地址转换为长整型和长整型转换为IPv6地址

18

我应该如何将IPv6转换为长整型或反之亦然?

目前我已经尝试了以下方法:

    public static long IPToLong(String addr) {
            String[] addrArray = addr.split("\\.");
            long num = 0;
            for (int i = 0; i < addrArray.length; i++) {
                    int power = 3 - i;

                    num += ((Integer.parseInt(addrArray[i], 16) % 256 * Math.pow(256, power)));
            }
            return num;
    }

    public static String longToIP(long ip) {
            return ((ip >> 24) & 0xFF) + "."
                    + ((ip >> 16) & 0xFF) + "."
                    + ((ip >> 8) & 0xFF) + "."
                    + (ip & 0xFF);

    }

这个解决方案是否正确,还是有什么我遗漏了的地方?

(如果这个解决方案同时适用于IPv4和IPv6就更好了)

4个回答

23

您也可以使用java.net.InetAddress。它适用于ipv4和ipv6(所有格式)。

原始答案已更新为使用无符号整数,根据OTrains的评论

public static BigInteger ipToBigInteger(String addr) throws UnknownHostException {
    InetAddress a = InetAddress.getByName(addr);
    byte[] bytes = a.getAddress();
    return new BigInteger(1, bytes);
}

5
如果你想要得到无符号的结果,就需要传入符号来保持它为正值(例如使用新的BigInteger(1, bytes))。否则,在IP地址范围的上半部分,你将会得到负数。 - OTrain

11

IPv6地址是一个128位的数字,如这里所描述的那样。在Java中,long类型用64位表示,因此需要另一种结构,例如BigDecimal或两个longs(一个包含两个long数组的容器或仅为两个long数组)来存储IPv6地址。

以下是一个示例(仅供参考):

public class Asd {

    public static long[] IPToLong(String addr) {
        String[] addrArray = addr.split(":");//a IPv6 adress is of form 2607:f0d0:1002:0051:0000:0000:0000:0004
        long[] num = new long[addrArray.length];
        
        for (int i=0; i<addrArray.length; i++) {
            num[i] = Long.parseLong(addrArray[i], 16);
        }
        long long1 = num[0];
        for (int i=1;i<4;i++) {
            long1 = (long1<<16) + num[i];
        }
        long long2 = num[4];
        for (int i=5;i<8;i++) {
            long2 = (long2<<16) + num[i];
        }
        
        long[] longs = {long2, long1};
        return longs;
    }
    
    
    public static String longToIP(long[] ip) {
        String ipString = "";
        for (long crtLong : ip) {//for every long: it should be two of them

            for (int i=0; i<4; i++) {//we display in total 4 parts for every long
                ipString = Long.toHexString(crtLong & 0xFFFF) + ":" + ipString;
                crtLong = crtLong >> 16;
            }
        }
        return ipString;
    
    }
    
    static public void main(String[] args) {
        String ipString = "2607:f0d0:1002:0051:0000:0000:0000:0004";
        long[] asd = IPToLong(ipString);
        
        System.out.println(longToIP(asd));
    }
}

好的,我会做到。那转换怎么样了?它转换正确了吗? - Testeross
问题如下:您试图将IPv4的代码适应到IPv6上,这是不正确的,因为在IPv4中,数字是以十进制编码的(IP地址“122.122.122.124”中的数字124是以十进制表示的),但在IPv6中,所有部分都是以十六进制编码的。这意味着,您将不得不决定IP地址的版本,并做出一些决策,以便为两个版本提供一种方法。 - V G
谢谢你的解释。不过我仍然不知道如何执行IPv6的转换。你能提供一些源或示例/教程的链接吗?我找不到任何有用的信息。 - Testeross
1
添加了一段可工作的代码,但请注意缺少某些检查(如无效字符串),因此这对于您的家庭作业来说是可以的,但对于生产环境来说测试不足。 - V G
处理零抑制和零折叠怎么办? - Trenin
显示剩余4条评论

8

IPv6地址无法存储在long中。您可以使用BigInteger替代long。

public static BigInteger ipv6ToNumber(String addr) {
    int startIndex=addr.indexOf("::");

    if(startIndex!=-1){


        String firstStr=addr.substring(0,startIndex);
        String secondStr=addr.substring(startIndex+2, addr.length());


        BigInteger first=ipv6ToNumber(firstStr);

        int x=countChar(addr, ':');

        first=first.shiftLeft(16*(7-x)).add(ipv6ToNumber(secondStr));

        return first;
    }


    String[] strArr = addr.split(":");

    BigInteger retValue = BigInteger.valueOf(0);
    for (int i=0;i<strArr.length;i++) {
        BigInteger bi=new BigInteger(strArr[i], 16);
        retValue = retValue.shiftLeft(16).add(bi);
    }
    return retValue;
}


public static String numberToIPv6(BigInteger ipNumber) {
    String ipString ="";
    BigInteger a=new BigInteger("FFFF", 16);

        for (int i=0; i<8; i++) {
            ipString=ipNumber.and(a).toString(16)+":"+ipString;

            ipNumber = ipNumber.shiftRight(16);
        }

    return ipString.substring(0, ipString.length()-1);

}

public static int countChar(String str, char reg){
    char[] ch=str.toCharArray();
    int count=0;
    for(int i=0; i<ch.length; ++i){
        if(ch[i]==reg){
            if(ch[i+1]==reg){
                ++i;
                continue;
            }
            ++count;
        }
    }
    return count;
}

1

Vinod的答案是正确的。但仍有一些可以改进的地方。

首先,在方法“countChar”中,“continue”应该被替换为“break”。

其次,必须考虑一些边界条件。

public static BigInteger ipv6ToNumber(String addr) {
    int startIndex = addr.indexOf("::");
    if (startIndex != -1) {
        String firstStr = addr.substring(0, startIndex);
        String secondStr = addr.substring(startIndex + 2, addr.length());
        BigInteger first = new BigInteger("0");
        BigInteger second = new BigInteger("0");
        if (!firstStr.equals("")) {
            int x = countChar(addr, ':');
            first = ipv6ToNumber(firstStr).shiftLeft(16 * (7 - x));
        }
        if (!secondStr.equals("")) {
            second = ipv6ToNumber(secondStr);
        }
        first = first.add(second);
        return first;
    }

    String[] strArr = addr.split(":");
    BigInteger retValue = BigInteger.valueOf(0);
    for (int i = 0; i < strArr.length; i++) {
        BigInteger bi = new BigInteger(strArr[i], 16);
        retValue = retValue.shiftLeft(16).add(bi);
    }
    return retValue;
}

public static int countChar(String str, char reg){
    char[] ch=str.toCharArray();
    int count=0;
    for(int i=0; i<ch.length; ++i){
        if(ch[i]==reg){
            if(ch[i+1]==reg){
                ++i;
                break;
            }
            ++count;
        }
    }
    return count;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接