Java 通过IP与子网掩码获取同网段IP地址列表

获取同网段IP?

对网络知识不牢靠的我,太小看了这东西,所以花费了我不少时间。
条件:

  • 一个IP地址
  • 一个子网掩码

像这样返回地址列表

[192.168.1.252, 192.168.1.253, 192.168.1.254, 192.168.1.255]
[192.168.1.252, 192.168.1.253, 192.168.1.255]
[192.168.1.253]

    public static void main(String[] args) {
        System.out.println(getLocalAreaIpList("192.168.1.254", "255.255.255.252"));
        System.out.println(getLocalAreaIpList("192.168.1.254", "255.255.255.252", true));
        System.out.println(getLocalAreaIpList("192.168.1.254", "255.255.255.252", true, true));
    }

我经历了些什么:

  • 格式判断
  • 转换IP为二进制数组
  • 计算子网数,主机数
  • 对二进制计算
  • 二进制转IP
  • 返回

格式判断

用到了一些东西, 如二进制转换与运算,正则表达式

正则, 用于判断IP和子网掩码是否合法

    /**
     * 正则表达式: 匹配IPV4地址字符串
     */
    private final static String IPV4 = "^((2(5[0-5]|[0-4]\\d))|[0-1]?\\d{1,2})(\\.((2(5[0-5]|[0-4]\\d))|[0-1]?\\d{1,2})){3}$";
    private final static String IP_MASK = "^((128|192)|2(24|4[08]|5[245]))(\\.(0|(128|192)|2((24)|(4[08])|(5[245])))){3}$";

    /**
     * 判断字符串是否为IPV4地址格式
     *
     * @param ip 要验证的ip字符串
     * @return 是 true/ 否 false
     */
    public static boolean isIp(String ip) {
        if (ip == null) return false;
        Pattern patt = Pattern.compile(IPV4);
        return patt.matcher(ip).matches();
    }

    /**
     * 判断字符串是否为IPV4 子网掩码格式
     *
     * @param mask 要验证的子网掩码字符串
     * @return 是 true/ 否 false
     */
    public static boolean isIpMask(String mask) {
        if (mask == null) return false;
        Pattern patt = Pattern.compile(IP_MASK);
        return patt.matcher(mask).matches();
    }

转换IP为二进制

这里有两个方法, 一个用于转二进制字符串数组,另一个是数值型二进制数组

   /**
     * 获取IP地址或掩码二进制数组
     * @param ip IP或子网掩码
     * @return 二进制数组如[11111111,11111111,11111111,11111111]
     */
    public static String [] getIpBinary(String ip){
        String[] strs = ip.split("\\.");
        for (int i = 0; i < 4; i++){
            strs[i] = Integer.toBinaryString(Integer.parseInt(strs[i]));
            if(strs[i].length() < 8) {
                StringBuilder zero = new StringBuilder();
                for (int j = 0; j < 8 - strs[i].length(); j++) zero.append("0");
                strs[i] = zero.toString() + strs[i];
            }
        }
        return strs;
    }

    /**
     * 将二进制字符串数组转换为byte数组,长度由第一个值的长度决定
     * @param binaryStrArr 二进制数组
     * @exception ArrayIndexOutOfBoundsException 如果数组二进制字符串长度不同,将会抛出异常
     * @return 如[1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0]
     */
    public static byte[] toBinary(String[] binaryStrArr){
        int bl = binaryStrArr[0].length();
        byte[] bytes = new byte[bl * binaryStrArr.length];
        for(int i = 0; i < binaryStrArr.length; i++){
            for (int j = 0; j < bl; j++) bytes[i * bl + j] = (byte) (binaryStrArr[i].charAt(j) == '1' ? 1 : 0);
        }
        return bytes;
    }

计算子网数,主机数

由于是IPv4 总长32, 子网掩码为1的累加,加起来就是子网掩码有效位;
主机位长度为32-子网掩码位数
最大主机数也即是 1 << 主机位数, 1位 0 ~ 1, 2位 0 ~ 3

		byte[] maskArr = toBinary(maskBinary);//二进制掩码数组
        byte[] ipArr = toBinary(ipBinary);//二进制IP数组
        int maskLen = 0;//子网掩码长度
        for (int i = 0; i < maskArr.length; i++) maskLen += maskArr[i];
//        int maskNumberLen = maskLen % 8;//子网位数,若为0 则8位全为主机号
//        System.out.println("子网号位数:" + maskNumberLen);
        int hostNumberLen = 32 - maskLen;//主机IP位数
//        System.out.println("主机号位数:" + hostNumberLen);
        int maxHost = 1 << hostNumberLen;

对二进制计算

这方法是对二进制数组进行增加某个值,用于IP生成时增加

    /**
     * 对二进制数组增加指定值
     * <p>如果增加的值超出此数组长度二进制最大表示值, 数组将重置为0, 从0开始增加</p>
     * @param binaryArray 二进制数组值应当全为1或0
     * @param plus 增加的数值10进制
     */
    public static void binaryArrayPlus(byte[] binaryArray, int plus){
        binaryArrayPlus(binaryArray, binaryArray.length - 1, plus);
    }

    /**
     * 对二进制数组增加指定值
     * <p>如果增加的值超出此数组长度二进制最大表示值, 数组将重置为0, 从0开始增加</p>
     * @param binaryArray 二进制数组值应当全为1或0
     * @param index 下标
     * @param plus 增加的数值10进制
     */
    private static void binaryArrayPlus(byte[] binaryArray, int index, int plus){
        if(index < 0) {
            binaryArray[0] = 0;
            return;
        }
        binaryArray[index] = (byte) (binaryArray[index] + 1);
        plus--;
        //如果进位,则递归进位
        if(binaryArray[index] > 1){
            binaryArrayPlus(binaryArray, index - 1, 1);
            binaryArray[index] = 0;
        }
        //如果增加的数超过1
        if(plus > 0) binaryArrayPlus(binaryArray, index, plus);
    }

二进制转IP

再就是将二进制数组转为IP字符串, 数学不好算法可能略差。

StringBuilder genIp = new StringBuilder();//生成的IP
//转换为IP地址
int decimal = 0;
for(int j = 0; j < 32; j++){
	decimal += mod[j] << (7 - j % 8);
	if(j != 0 && (j+1) % 8 == 0){
		if(genIp.length() == 0) genIp.append(decimal);
		else genIp.append(".").append(decimal);
		decimal = 0;
	}
}
//genIp.toString();//转换的IP

返回,调用核心方法

调用方法返回IP列表

    /**
     * 获取局域网内的所有IP, 包含参数地址, 包含首尾地址
     *
     * @param ip             用作查找基础的IP,返回此IP网段的地址列表
     * @param mask           子网掩码
     * @return IP list 或 null, 如果地址非法则返回null
     */
    public static List<String> getLocalAreaIpList(String ip, String mask){
        return getLocalAreaIpList(ip, mask, false);
    }

    /**
     * 获取局域网内的所有IP, 包含首尾地址
     *
     * @param ip             用作查找基础的IP,返回此IP网段的地址列表
     * @param mask           子网掩码
     * @param containParamIp 返回结果是否包含传入参数的IP
     * @return IP list 或 null, 如果地址非法则返回null
     */
    public static List<String> getLocalAreaIpList(String ip, String mask, boolean containParamIp ){
        return getLocalAreaIpList(ip, mask, containParamIp, false);
    }

    /**
     * 获取局域网内的所有IP
     *
     * @param ip             用作查找基础的IP,返回此IP网段的地址列表
     * @param mask           子网掩码
     * @param containParamIp 返回结果是否包含传入参数的IP
     * @param ignoreFirstAndLastIp 是否忽略首尾IP,(网段地址与广播地址)
     * @return IP list 或 null, 如果地址非法则返回null
     */
    public static List<String> getLocalAreaIpList(String ip, String mask, boolean containParamIp, boolean ignoreFirstAndLastIp) {
        if (!isIp(ip) || !isIpMask(mask)) return null;//非法ip或子网掩码
        String[] maskBinary = getIpBinary(mask);//子网掩码二进制数组
        //[11000000, 10101000, 00000000, 11111110]
        String[] ipBinary = getIpBinary(ip);//IP地址二进制数组
        //取同网段部分
        byte[] maskArr = toBinary(maskBinary);//二进制掩码数组
        byte[] ipArr = toBinary(ipBinary);//二进制IP数组
        int maskLen = 0;//子网掩码长度
        for (int i = 0; i < maskArr.length; i++) maskLen += maskArr[i];
//        int maskNumberLen = maskLen % 8;//子网位数,若为0 则8位全为主机号
//        System.out.println("子网号位数:" + maskNumberLen);
        int hostNumberLen = 32 - maskLen;//主机IP位数
//        System.out.println("主机号位数:" + hostNumberLen);
        int maxHost = 1 << hostNumberLen;
//        System.out.println("支持主机个数:" + maxHost);
        byte[] mod = new byte[32];//同网段二进制数组
        for(int i = 0; i < 32; i ++) mod[i] = (byte)(maskArr[i] & ipArr[i]);
        List<String> ipList = new ArrayList<>(maxHost);
        StringBuilder genIp = new StringBuilder();//生成的IP
        for(int i = 0; i < maxHost; i++){
            //转换为IP地址
            int decimal = 0;
            for(int j = 0; j < 32; j++){
                decimal += mod[j] << (7 - j % 8);
                if(j != 0 && (j+1) % 8 == 0){
                    if(genIp.length() == 0) genIp.append(decimal);
                    else genIp.append(".").append(decimal);
                    decimal = 0;
                }
            }
            binaryArrayPlus(mod, 1);//从0开始增加maxHost次
//            System.out.println(genIp);//生成的IP
            String generateIp = genIp.toString();
            genIp.delete(0, genIp.length());//清空
            if(ignoreFirstAndLastIp && (i == 0 || i == maxHost - 1)) continue;//跳过首位地址
            if(!containParamIp && generateIp.equals(ip)) continue;//跳过相同地址
            ipList.add(generateIp);
        }
        return ipList;
    }

Last changed 2020-12-11 , cause if(!containParamIp && generateIp.equals(ip)) continue; 判断错误

  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值