上次实现的IP地址段匹配库,当数据量过大时, 匹配起来实在是太慢了,所以使用算法重新实现了一下。
ip地址段匹配库1.0
IP地址段实体类:
import java.math.BigInteger;
import java.util.Objects;
/**
* @author chun
* @date 2023/12/27 15:17
*/
public class IPRanges {
private BigInteger start;
private BigInteger end;
public IPRanges(BigInteger start, BigInteger end) {
this.start = start;
this.end = end;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
IPRanges ipRanges = (IPRanges) o;
return Objects.equals(start, ipRanges.start) && Objects.equals(end, ipRanges.end);
}
@Override
public int hashCode() {
return Objects.hash(start, end);
}
public BigInteger getStart() {
return start;
}
public void setStart(BigInteger start) {
this.start = start;
}
public BigInteger getEnd() {
return end;
}
public void setEnd(BigInteger end) {
this.end = end;
}
}
匹配地址匹配工具类
import com.chun.bean.IPRanges;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.net.util.SubnetUtils;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.math.BigInteger;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
/**
* @author chun
* @date 2023/12/27 14:23
*/
@Slf4j
public class IPUtils {
private static List<IPRanges> ipRanges = new ArrayList<>();
public static void readIpRangesList(String fullName) throws IOException {
BufferedReader fileReader = null;
try {
File file = new File(fullName);
fileReader = new BufferedReader(new FileReader(file));
String line;
while ((line = fileReader.readLine()) != null) {
setIpRanges(line);
}
buildMatchingArray();
} finally {
try {
fileReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static void readIpRangesList(File[] files) throws IOException {
for (File file : files) {
BufferedReader fileReader = null;
try {
fileReader = new BufferedReader(new FileReader(file));
String line;
while ((line = fileReader.readLine()) != null) {
setIpRanges(line);
}
} finally {
try {
fileReader.close();
} catch (IOException e) {
log.error("", e);
}
}
}
buildMatchingArray();
}
private static void buildMatchingArray() {
// 对IP地址段进行排序
ipRanges.sort(Comparator.comparing(IPRanges::getStart).thenComparing(IPRanges::getEnd));
// 处理重叠的IP地址段
List<IPRanges> mergedRanges = new ArrayList<>();
IPRanges currentRange = ipRanges.get(0);
for (int i = 1; i < ipRanges.size(); i++) {
IPRanges nextRange = ipRanges.get(i);
if (currentRange.getEnd().compareTo(nextRange.getStart()) >= 0) { // 当前范围与下一个范围重叠,合并
if (currentRange.getEnd().compareTo(nextRange.getEnd()) < 0) {
currentRange = new IPRanges(currentRange.getStart(), nextRange.getEnd());
} else {//当前范围包含下一个范围,保留当前
}
} else {// 当前范围与下一个范围不重叠,将当前范围添加到结果列表中
mergedRanges.add(currentRange);
currentRange = nextRange;
}
}
// 添加最后一个范围
mergedRanges.add(currentRange);
ipRanges = mergedRanges;
}
public static boolean commonBinarySearch(String ip) {
BigInteger ip2BigInteger = getIp2BigInteger(ip);
int low = 0;
int high = ipRanges.size() - 1;
int middle = 0; //定义middle
//小于第一个的start,大于最后一个的end,就算不在范围内
if (ip2BigInteger.compareTo(ipRanges.get(low).getStart()) < 0 || ip2BigInteger.compareTo(ipRanges.get(high).getEnd()) > 0 || low > high) {
return false;
}
while (low <= high) {
middle = (low + high) / 2;
//先判断是否在中间值的范围内
BigInteger ipS = ipRanges.get(middle).getStart();
BigInteger ipE = ipRanges.get(middle).getEnd();
// if (range[0].equals("112.45.150.66")){
// System.out.println(111);
// }
if (ipExistsInRange(ip2BigInteger, ipS, ipE)) {
return true;
}
if (ipS.compareTo(ip2BigInteger) > 0) {
//比关键字大则关键字在左区域
high = middle - 1;
} else if (ipS.compareTo(ip2BigInteger) < 0) {
//比关键字小则关键字在右区域
low = middle + 1;
} else {//等于
return true;
}
}
return false; //最后仍然没有找到,则返回-1
}
private static void setIpRanges(String cidrAddress) throws UnknownHostException {
String startAddress = "";
String endAddress = "";
try {
String[] split = cidrAddress.split("\\/");
SubnetUtils utils = new SubnetUtils(cidrAddress);
SubnetUtils.SubnetInfo subnetInfo = utils.getInfo();
if (split[1].equals("32")) {
startAddress = split[0];
endAddress = split[0];
} else {
startAddress = subnetInfo.getLowAddress();
endAddress = subnetInfo.getHighAddress();
}
System.out.println("IP CIDR=" + cidrAddress + ",IP范围:[" + startAddress + ", " + endAddress + "]");
} catch (IllegalArgumentException e) {
InetAddress networkAddress = InetAddress.getByName(cidrAddress.split("/")[0]);
int subnetPrefixLength = Integer.parseInt(cidrAddress.split("/")[1]);
BigInteger start = getStartAddress(networkAddress, subnetPrefixLength);
BigInteger end = getEndAddress(start, subnetPrefixLength);
startAddress = getAddressFromBigInteger(start);
endAddress = getAddressFromBigInteger(end);
System.out.println("IP CIDR=" + cidrAddress + ",IP范围:[" + startAddress + ", " + endAddress + "]");
}
ipRanges.add(new IPRanges(getIp2BigInteger(startAddress), getIp2BigInteger(endAddress)));
}
private static String getAddressFromBigInteger(BigInteger address) throws UnknownHostException {
byte[] bytes = address.toByteArray();
InetAddress inetAddress;
if (bytes.length == 16) {
inetAddress = InetAddress.getByAddress(bytes);
} else {
byte[] paddedBytes = new byte[16];
System.arraycopy(bytes, 0, paddedBytes, 16 - bytes.length, bytes.length);
inetAddress = InetAddress.getByAddress(paddedBytes);
}
return inetAddress.getHostAddress();
}
private static BigInteger getStartAddress(InetAddress networkAddress, int subnetPrefixLength) {
ByteBuffer buffer = ByteBuffer.wrap(networkAddress.getAddress());
BigInteger start = new BigInteger(1, buffer.array());
return start.shiftRight(128 - subnetPrefixLength).shiftLeft(128 - subnetPrefixLength);
}
private static BigInteger getEndAddress(BigInteger start, int subnetPrefixLength) {
BigInteger hostCount = BigInteger.ONE.shiftLeft(128 - subnetPrefixLength);
return start.add(hostCount).subtract(BigInteger.ONE);
}
private static boolean ipExistsInRange(BigInteger ip2BigInteger, BigInteger ipS, BigInteger ipE) {
return ipS.compareTo(ip2BigInteger) <= 0 && ip2BigInteger.compareTo(ipE) <= 0;
}
private static long getIp2long(String ip) {
ip = ip.trim();
String[] ips = ip.split("\\.");
long ip2long = 0L;
for (int i = 0; i < 4; ++i) {
ip2long = ip2long << 8 | Integer.parseInt(ips[i]);
}
return ip2long;
}
private static BigInteger getIp2BigInteger(String ip) {
try {
InetAddress inetAddress = InetAddress.getByName(ip.trim());
byte[] bytes = inetAddress.getAddress();
BigInteger bigInt = new BigInteger(1, bytes);
return bigInt;
} catch (UnknownHostException e) {
// 处理无效的IP地址
System.out.println("Invalid IP address: " + ip);
return null; // 或者抛出异常,具体根据需求而定
}
}
}
使用方式和1.0一样,使用二分查找重新实现一下,并且支持对传入的IP地址段进行处理,对与IP地址段范围重叠的部分进行聚合去重。