This commit is contained in:
emptypoint 2023-02-12 14:54:24 +08:00
parent 5753444de2
commit 74ca8eb1d0
2 changed files with 169 additions and 98 deletions

View File

@ -17,7 +17,19 @@ import java.util.regex.Matcher;
* *
* <p>pr自https://gitee.com/loolly/hutool/pulls/161</p> * <p>pr自https://gitee.com/loolly/hutool/pulls/161</p>
* *
* <p>名词解释
* <ul>
* <li>ip字符串点分十进制形如xxx.xxx.xxx.xxx</li>
* <li>ip的Long类型有效位32位每8位可以转为一个十进制数例如0xC0A802FA 转为点分十进制是192.168.2.250</li>
* <li>掩码地址点分十进制例如255.255.255.0</li>
* <li>掩码位int类型例如 24, 它代表的掩码地址为255.255.255.0掩码位和掩码地址的相互转换请使用 {@link MaskBit}</li>
* <li>CIDR无类域间路由形如xxx.xxx.xxx.xxx/掩码位</li>
* <li>全量地址区间内所有ip地址包含区间两端</li>
* <li>可用地址区间内所有ip地址但是不包含区间两端</li>
* </ul>
*
* @author ZhuKun * @author ZhuKun
* @author emptypoint
* @since 5.4.1 * @since 5.4.1
*/ */
public class Ipv4Util { public class Ipv4Util {
@ -43,22 +55,57 @@ public class Ipv4Util {
public static final int IP_MASK_MAX = 32; public static final int IP_MASK_MAX = 32;
/** /**
* 格式化IP段 * 最小掩码位
*/
public static final int IP_MASK_MIN = 1;
/**
* A类私有地址的最小值
*/
public static final long A_INNER_IP_LONG_BEGIN = ipv4ToLong("10.0.0.0");
/**
* A类私有地址的最大值
*/
public static final long A_INNER_IP_LONG_END = ipv4ToLong("10.255.255.255");
/**
* B类私有地址的最小值
*/
public static final long B_INNER_IP_LONG_BEGIN = ipv4ToLong("172.16.0.0");
/**
* B类私有地址的最大值
*/
public static final long B_INNER_IP_LONG_END = ipv4ToLong("172.31.255.255");
/**
* C类私有地址的最小值
*/
public static final long C_INNER_IP_LONG_BEGIN = ipv4ToLong("192.168.0.0");
/**
* C类私有地址的最大值
*/
public static final long C_INNER_IP_LONG_END = ipv4ToLong("192.168.255.255");
/**
* 根据 ip地址 掩码地址 获得 CIDR格式字符串
* *
* @param ip IP地址 * @param ip IP地址点分十进制xxx.xxx.xxx.xxx
* @param mask 掩码 * @param mask 掩码地址点分十进制255.255.255.0
* @return 返回xxx.xxx.xxx.xxx/mask的格式 * @return 返回 {@literal xxx.xxx.xxx.xxx/掩码位} 的格式
*/ */
public static String formatIpBlock(final String ip, final String mask) { public static String formatIpBlock(final String ip, final String mask) {
return ip + IP_MASK_SPLIT_MARK + getMaskBitByMask(mask); return ip + IP_MASK_SPLIT_MARK + getMaskBitByMask(mask);
} }
/** /**
* 智能转换IP地址集合 * 智能获取指定区间内的所有IP地址
* *
* @param ipRange IP段支持X.X.X.X-X.X.X.X或X.X.X.X/X * @param ipRange IP区间支持 {@literal X.X.X.X-X.X.X.X} {@literal X.X.X.X/X}
* @param isAll true:全量地址false:可用地址仅在ipRange为X.X.X.X/X时才生效 * @param isAll true:全量地址false:可用地址该参数仅在ipRange为X.X.X.X/X时才生效
* @return IP集 * @return 区间内的所有IP地址点分十进制格式
*/ */
public static List<String> list(final String ipRange, final boolean isAll) { public static List<String> list(final String ipRange, final boolean isAll) {
if (ipRange.contains(IP_SPLIT_MARK)) { if (ipRange.contains(IP_SPLIT_MARK)) {
@ -75,12 +122,12 @@ public class Ipv4Util {
} }
/** /**
* 根据IP地址子网掩码获取IP地址区间 * 根据 IP地址 掩码位数 获取 子网所有ip地址
* *
* @param ip IP地址 * @param ip IP地址点分十进制
* @param maskBit 掩码位例如2432 * @param maskBit 掩码位例如2432
* @param isAll true:全量地址false:可用地址 * @param isAll true:全量地址false:可用地址
* @return 区间地址 * @return 子网所有ip地址
*/ */
public static List<String> list(final String ip, final int maskBit, final boolean isAll) { public static List<String> list(final String ip, final int maskBit, final boolean isAll) {
if (maskBit == IP_MASK_MAX) { if (maskBit == IP_MASK_MAX) {
@ -107,11 +154,11 @@ public class Ipv4Util {
} }
/** /**
* 得到IP地址区间 * 获得 指定区间内 所有ip地址
* *
* @param ipFrom 开始IP * @param ipFrom 开始IP包含点分十进制
* @param ipTo 结束IP * @param ipTo 结束IP包含点分十进制
* @return 区间地址 * @return 区间内所有ip地址
*/ */
public static List<String> list(final String ipFrom, final String ipTo) { public static List<String> list(final String ipFrom, final String ipTo) {
// 确定ip数量 // 确定ip数量
@ -154,34 +201,34 @@ public class Ipv4Util {
} }
/** /**
* 根据long值获取ip v4地址xx.xx.xx.xx * 根据 ip的long值 获取 ip字符串xxx.xxx.xxx.xxx
* *
* @param longIP IP的long表示形式 * @param longIp IP的long表示形式
* @return IP V4 地址 * @return 点分十进制ip地址
*/ */
public static String longToIpv4(final long longIP) { public static String longToIpv4(final long longIp) {
final StringBuilder sb = StrUtil.builder(); final StringBuilder sb = StrUtil.builder();
// 直接右移24位 // 直接右移24位
sb.append(longIP >> 24 & 0xFF); sb.append(longIp >> 24 & 0xFF);
sb.append(CharUtil.DOT); sb.append(CharUtil.DOT);
// 将高8位置0然后右移16位 // 将高8位置0然后右移16位
sb.append(longIP >> 16 & 0xFF); sb.append(longIp >> 16 & 0xFF);
sb.append(CharUtil.DOT); sb.append(CharUtil.DOT);
sb.append(longIP >> 8 & 0xFF); sb.append(longIp >> 8 & 0xFF);
sb.append(CharUtil.DOT); sb.append(CharUtil.DOT);
sb.append(longIP & 0xFF); sb.append(longIp & 0xFF);
return sb.toString(); return sb.toString();
} }
/** /**
* 根据ip地址(xxx.xxx.xxx.xxx)计算出long型的数据 * ip字符串 转换为 long值
* 方法别名inet_aton * <p>方法别名inet_aton</p>
* *
* @param strIP IP V4 地址 * @param strIp ip地址点分十进制xxx.xxx.xxx.xxx
* @return long值 * @return ip的long值
*/ */
public static long ipv4ToLong(final String strIP) { public static long ipv4ToLong(final String strIp) {
final Matcher matcher = PatternPool.IPV4.matcher(strIP); final Matcher matcher = PatternPool.IPV4.matcher(strIp);
if (matcher.matches()) { if (matcher.matches()) {
return matchAddress(matcher); return matchAddress(matcher);
} }
@ -189,11 +236,11 @@ public class Ipv4Util {
} }
/** /**
* 根据 ip/掩码位 计算IP段的起始IP字符串型 * 根据 ip 掩码位 获取 子网的起始IP字符串型
* 方法别名inet_ntoa * <p>方法别名inet_ntoa</p>
* *
* @param ip 给定的IP如218.240.38.69 * @param ip 给定的IP点分十进制xxx.xxx.xxx.xxx
* @param maskBit 给定的掩码位30 * @param maskBit 给定的掩码位30
* @return 起始IP的字符串表示 * @return 起始IP的字符串表示
*/ */
public static String getBeginIpStr(final String ip, final int maskBit) { public static String getBeginIpStr(final String ip, final int maskBit) {
@ -201,10 +248,10 @@ public class Ipv4Util {
} }
/** /**
* 根据 ip/掩码位 计算IP段的起始IPLong型 * 根据 ip 掩码位 获取 子网的起始IPLong型
* *
* @param ip 给定的IP如218.240.38.69 * @param ip 给定的IP点分十进制xxx.xxx.xxx.xxx
* @param maskBit 给定的掩码位30 * @param maskBit 给定的掩码位30
* @return 起始IP的长整型表示 * @return 起始IP的长整型表示
*/ */
public static Long getBeginIpLong(final String ip, final int maskBit) { public static Long getBeginIpLong(final String ip, final int maskBit) {
@ -212,10 +259,10 @@ public class Ipv4Util {
} }
/** /**
* 根据 ip/掩码位 计算IP段的终止IP字符串型 * 根据 ip 掩码位 获取 子网的终止IP字符串型
* *
* @param ip 给定的IP如218.240.38.69 * @param ip 给定的IP点分十进制xxx.xxx.xxx.xxx
* @param maskBit 给定的掩码位30 * @param maskBit 给定的掩码位30
* @return 终止IP的字符串表示 * @return 终止IP的字符串表示
*/ */
public static String getEndIpStr(final String ip, final int maskBit) { public static String getEndIpStr(final String ip, final int maskBit) {
@ -223,7 +270,18 @@ public class Ipv4Util {
} }
/** /**
* 根据子网掩码转换为掩码位 * 根据 ip 掩码位 获取 子网的终止IPLong型
*
* @param ip 给定的IP点分十进制xxx.xxx.xxx.xxx
* @param maskBit 给定的掩码位30
* @return 终止IP的长整型表示
*/
public static Long getEndIpLong(final String ip, final int maskBit) {
return getBeginIpLong(ip, maskBit) + ~ipv4ToLong(getMaskByMaskBit(maskBit));
}
/**
* 子网掩码 转换为 掩码位
* *
* @param mask 掩码的点分十进制表示例如 255.255.255.0 * @param mask 掩码的点分十进制表示例如 255.255.255.0
* @return 掩码位例如 24 * @return 掩码位例如 24
@ -238,11 +296,11 @@ public class Ipv4Util {
} }
/** /**
* 计算子网大小 * 获取 子网内的 地址总数
* *
* @param maskBit 掩码位 * @param maskBit 掩码位取值范围[2, {@link #IP_MASK_MAX}]
* @param isAll true:全量地址false:可用地址 * @param isAll true:全量地址false:可用地址
* @return 地址总数 * @return 子网内地址总数
*/ */
public static int countByMaskBit(final int maskBit, final boolean isAll) { public static int countByMaskBit(final int maskBit, final boolean isAll) {
//如果是可用地址的情况掩码位小于等于0或大于等于32则可用地址为0 //如果是可用地址的情况掩码位小于等于0或大于等于32则可用地址为0
@ -255,21 +313,21 @@ public class Ipv4Util {
} }
/** /**
* 根据掩码位获取掩码 * 根据 掩码位 获取 掩码地址
* *
* @param maskBit 掩码位 * @param maskBit 掩码位24取值范围[{@link #IP_MASK_MIN}, {@link #IP_MASK_MAX}]
* @return 掩码 * @return 掩码地址点分十进制:255.255.255.0
*/ */
public static String getMaskByMaskBit(final int maskBit) { public static String getMaskByMaskBit(final int maskBit) {
return MaskBit.get(maskBit); return MaskBit.get(maskBit);
} }
/** /**
* 根据开始IP与结束IP计算掩码 * 根据 开始IP 结束IP 获取 掩码地址
* *
* @param fromIp 开始IP * @param fromIp 开始IP包含点分十进制
* @param toIp 结束IP * @param toIp 结束IP包含点分十进制
* @return 掩码x.x.x.x * @return 掩码地址点分十进制
*/ */
public static String getMaskByIpRange(final String fromIp, final String toIp) { public static String getMaskByIpRange(final String fromIp, final String toIp) {
final long toIpLong = ipv4ToLong(toIp); final long toIpLong = ipv4ToLong(toIp);
@ -286,10 +344,10 @@ public class Ipv4Util {
} }
/** /**
* 计算IP区间有多少个IP * 获得 指定区间内的 ip数量
* *
* @param fromIp 开始IP * @param fromIp 开始IP包含点分十进制
* @param toIp 结束IP * @param toIp 结束IP包含点分十进制
* @return IP数量 * @return IP数量
*/ */
public static int countByIpRange(final String fromIp, final String toIp) { public static int countByIpRange(final String fromIp, final String toIp) {
@ -320,7 +378,7 @@ public class Ipv4Util {
/** /**
* 判断掩码位是否合法 * 判断掩码位是否合法
* *
* @param maskBit 掩码位例如 24 * @param maskBit 掩码位有效范围[{@link #IP_MASK_MIN}, {@link #IP_MASK_MAX}]
* @return true掩码位合法false掩码位不合法 * @return true掩码位合法false掩码位不合法
*/ */
public static boolean isMaskBitValid(final int maskBit) { public static boolean isMaskBitValid(final int maskBit) {
@ -337,47 +395,25 @@ public class Ipv4Util {
* </pre> * </pre>
* 当然还有127这个网段是环回地址 * 当然还有127这个网段是环回地址
* *
* @param ipAddress IP地址 * @param ipAddress IP地址点分十进制
* @return 是否为内网IP * @return 是否为内网IP
* @since 5.7.18 * @since 5.7.18
*/ */
public static boolean isInnerIP(final String ipAddress) { public static boolean isInnerIP(final String ipAddress) {
final boolean isInnerIp;
final long ipNum = ipv4ToLong(ipAddress); final long ipNum = ipv4ToLong(ipAddress);
return isBetween(ipNum, A_INNER_IP_LONG_BEGIN, A_INNER_IP_LONG_END)
final long aBegin = ipv4ToLong("10.0.0.0"); || isBetween(ipNum, B_INNER_IP_LONG_BEGIN, B_INNER_IP_LONG_END)
final long aEnd = ipv4ToLong("10.255.255.255"); || isBetween(ipNum, C_INNER_IP_LONG_BEGIN, C_INNER_IP_LONG_END)
|| LOCAL_IP.equals(ipAddress);
final long bBegin = ipv4ToLong("172.16.0.0");
final long bEnd = ipv4ToLong("172.31.255.255");
final long cBegin = ipv4ToLong("192.168.0.0");
final long cEnd = ipv4ToLong("192.168.255.255");
isInnerIp = isInner(ipNum, aBegin, aEnd) || isInner(ipNum, bBegin, bEnd) || isInner(ipNum, cBegin, cEnd) || LOCAL_IP.equals(ipAddress);
return isInnerIp;
} }
//-------------------------------------------------------------------------------- Private method start //-------------------------------------------------------------------------------- Private method start
/** /**
* 根据 ip/掩码位 计算IP段的终止IPLong型 * 将匹配到的Ipv4地址转为Long类型
* 此接口返回负数请使用转成字符串后再转Long型
*
* @param ip 给定的IP如218.240.38.69
* @param maskBit 给定的掩码位如30
* @return 终止IP的长整型表示
*/
public static Long getEndIpLong(final String ip, final int maskBit) {
return getBeginIpLong(ip, maskBit)
+ ~ipv4ToLong(getMaskByMaskBit(maskBit));
}
/**
* 将匹配到的Ipv4地址的4个分组分别处理
* *
* @param matcher 匹配到的Ipv4正则 * @param matcher 匹配到的Ipv4正则
* @return ipv4对应long * @return ip的long值
*/ */
private static long matchAddress(final Matcher matcher) { private static long matchAddress(final Matcher matcher) {
long addr = 0; long addr = 0;
@ -388,14 +424,14 @@ public class Ipv4Util {
} }
/** /**
* 指定IP的long是否在指定范围内 * 指定IP是否在指定范围内
* *
* @param userIp 用户IP * @param userIp 用户IP
* @param begin 开始IP * @param begin 开始IP包含
* @param end 结束IP * @param end 结束IP包含
* @return 是否在范围内 * @return 是否在范围内
*/ */
private static boolean isInner(final long userIp, final long begin, final long end) { private static boolean isBetween(final long userIp, final long begin, final long end) {
return (userIp >= begin) && (userIp <= end); return (userIp >= begin) && (userIp <= end);
} }
//-------------------------------------------------------------------------------- Private method end //-------------------------------------------------------------------------------- Private method end

View File

@ -4,8 +4,6 @@ import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
import org.junit.function.ThrowingRunnable; import org.junit.function.ThrowingRunnable;
import java.util.List;
public class Ipv4UtilTest { public class Ipv4UtilTest {
@Test @Test
@ -28,10 +26,16 @@ public class Ipv4UtilTest {
@Test @Test
public void longToIpTest() { public void longToIpTest() {
final String ip = "192.168.1.255"; testLongToIp("192.168.1.255");
final long ipLong = Ipv4Util.ipv4ToLong(ip); testLongToIp("0.0.0.0");
final String ipv4 = Ipv4Util.longToIpv4(ipLong); testLongToIp("0.0.0.255");
Assert.assertEquals(ip, ipv4); testLongToIp("0.0.255.255");
testLongToIp("0.255.255.255");
testLongToIp("255.255.255.255");
testLongToIp("255.255.255.0");
testLongToIp("255.255.0.0");
testLongToIp("255.0.0.0");
testLongToIp("0.255.255.0");
} }
@Test @Test
@ -44,9 +48,24 @@ public class Ipv4UtilTest {
@Test @Test
public void listTest(){ public void listTest(){
final int maskBit = Ipv4Util.getMaskBitByMask("255.255.255.0"); final String ip = "192.168.100.2";
final List<String> list = Ipv4Util.list("192.168.100.2", maskBit, false); testGenerateIpList(ip, 22, false);
Assert.assertEquals(254, list.size()); testGenerateIpList(ip, 22, true);
testGenerateIpList(ip, 24, false);
testGenerateIpList(ip, 24, true);
testGenerateIpList(ip, 26, false);
testGenerateIpList(ip, 26, true);
testGenerateIpList(ip, 30, false);
testGenerateIpList(ip, 30, true);
testGenerateIpList(ip, 31, false);
testGenerateIpList(ip, 31, true);
testGenerateIpList(ip, 32, false);
testGenerateIpList(ip, 32, true);
testGenerateIpList("10.1.0.1", "10.2.1.2"); testGenerateIpList("10.1.0.1", "10.2.1.2");
@ -90,8 +109,9 @@ public class Ipv4UtilTest {
@Test @Test
public void isMaskBitValidTest() { public void isMaskBitValidTest() {
final boolean maskBitValid = Ipv4Util.isMaskBitValid(32); for (int i = 1; i < 32; i++) {
Assert.assertTrue("掩码位合法检验", maskBitValid); Assert.assertTrue("掩码位非法:" + i, Ipv4Util.isMaskBitValid(i));
}
} }
@Test @Test
@ -119,4 +139,19 @@ public class Ipv4UtilTest {
Ipv4Util.list(fromIp, toIp).size() Ipv4Util.list(fromIp, toIp).size()
); );
} }
@SuppressWarnings("SameParameterValue")
private void testGenerateIpList(final String ip, final int maskBit, final boolean isAll) {
Assert.assertEquals(
Ipv4Util.countByMaskBit(maskBit, isAll),
Ipv4Util.list(ip, maskBit, isAll).size()
);
}
private static void testLongToIp(final String ip){
final long ipLong = Ipv4Util.ipv4ToLong(ip);
final String ipv4 = Ipv4Util.longToIpv4(ipLong);
Assert.assertEquals(ip, ipv4);
}
} }