Java中IP地址筛选器内存中数据结构的最佳选择
我有一个CIDR格式的文件,像这样Java中IP地址筛选器内存中数据结构的最佳选择,java,filter,ip,in-memory,Java,Filter,Ip,In Memory,我有一个CIDR格式的文件,像这样192.168.1.0/24,它被转换成两列结构 3232236030 3232235777 每个字符串IP地址转换都与以下代码一起发生: String subnet = "192.168.1.0/24"; SubnetUtils utils = new SubnetUtils(subnet); Inet4Address a = (Inet4Address) InetAddress.getByName(utils.getInfo().ge
192.168.1.0/24
,它被转换成两列结构
3232236030 3232235777
每个字符串IP地址转换都与以下代码一起发生:
String subnet = "192.168.1.0/24";
SubnetUtils utils = new SubnetUtils(subnet);
Inet4Address a = (Inet4Address) InetAddress.getByName(utils.getInfo().getHighAddress());
long high = bytesToLong(a.getAddress());
Inet4Address b = (Inet4Address) InetAddress.getByName(utils.getInfo().getLowAddress());
long low = bytesToLong(b.getAddress());
private static long bytesToLong(byte[] address) {
long ipnum = 0;
for (int i = 0; i < 4; ++i) {
long y = address[i];
if (y < 0) {
y += 256;
}
ipnum += y << ((3 - i) * 8);
}
return ipnum;
}
String subnet=“192.168.1.0/24”;
SubnetUtils utils=新的SubnetUtils(子网);
Inet4Address a=(Inet4Address)InetAddress.getByName(utils.getInfo().getHighAddress());
long high=bytestolog(a.getAddress());
Inet4Address b=(Inet4Address)InetAddress.getByName(utils.getInfo().getLowAddress());
long low=bytestolog(b.getAddress());
专用静态长bytestolog(字节[]地址){
长ipnum=0;
对于(int i=0;i<4;++i){
长y=地址[i];
if(y<0){
y+=256;
}
ipnum+=y我将使用一个int排序数组(基址)和另一个大小相同的数组(结束地址)。这将使用5M*8=40 MB。第一个IP是基址,第二个IP是范围内的最后一个地址。您需要删除交点
若要查找某个地址是否被过滤为二进制搜索O(logn),若不是精确匹配,请检查它是否小于(或等于)上限。这是答案的开头,我会在有更多空闲时间时返回
设置:
按起始编号对范围进行排序
由于这些是IP地址,我假设所有范围都不重叠。如果有重叠,您可能应该运行列表合并范围并修剪不必要的范围(例如,如果您有范围1-10,您可以修剪范围5-7)。
要合并或修剪,请执行此操作(假设范围a紧跟在范围b之前):
如果b.end
如果b.starta.end,则可以合并范围a和b。设置a.end=b.end,然后删除范围b
当涉及到它时,我只需要知道IP是否存在于5M范围内
我会考虑一个n元树,其中n=256,从虚线地址而不是转换的整数中工作。< /P>
顶层是256个对象的数组。
null
条目表示“否”,没有包含地址的范围,因此在您的示例中,192.168.1.0/24
array[192]将包含一个对象,但是array[100]可能为null,因为没有为任何100.x.x.x/n定义范围
存储的对象包含(引用)另一个数组[256]和一个范围说明符,只设置其中一个,因此192.0.0.0/8
将以一个范围说明符结束,该范围说明符指示要过滤该范围内的所有地址。这将允许像192.255.0.0/10
这样的事情,其中地址的前10位是有效的1100 0000 11xx xxxx
——否则你需要检查二级数组中的下一个八位元
最初将重叠范围(如果有)合并到更大的范围中…例如,3..10
和7..16
变为3..16
…允许这样做,因为您不需要将给定的IP与定义它的范围相关联
这需要不超过8次比较。每个八位字节最初直接用作索引,然后是null比较,终端节点比较(是范围还是指向下一个树级别的指针)
如果每个IP地址都在一个筛选范围内,最坏情况下的内存消耗理论上是4 GB(256^4)
,但当然这会合并到一个范围内,因此实际上只有一个范围对象。更现实的最坏情况可能更像(256^3)
或16.7 MB。在实际使用中,每个级别上的大多数阵列[256]节点可能都是空的
这本质上类似于哈夫曼/前缀编码。最短的不同前缀可以在找到答案(一个范围)后立即终止,因此您通常会得到<4
比较的平均值。我在项目中发现了这种二进制切分算法:
公共IpRange isInRange(长地址){
检查重建();
if(mergedRanges.length==0){
返回(空);
}
//辅助二进制切分
int-bottom=0;
int top=mergedRanges.length-1;
int电流=-1;
while(顶部>=0&&bottom 如果(top>=0&&bottom
除非你过滤的是非常大的N个地址,否则这都是字符串比较,执行速度会非常快。你不需要基于高/低阶位和所有那些复杂的Jazz构建二叉树
String subnet = "192.168.1.0/24";
SubnetUtils utils = new SubnetUtils(subnet);
//...
//for each subnet, create a SubnetUtils object
Set<SubnetUtils> subnets = getAllSubnets();
//...
String subnet=“192.168.1.0/24”;
SubnetUtils utils=新的SubnetUtils(子网);
//...
//对于每个子网,创建SubnetUtils对象
Set subnets=getAllSubnets();
//...
使用番石榴谓词筛选不在子网范围内的IP地址:
Set<String> ipAddresses = getIpAddressesToFilter();
Set<String> ipAddressesInRange =
Sets.filter(ipAddresses, filterIpsBySubnet(subnets))
Predicate<String> filterIpsBySubnet(final Set<SubnetUtils> subnets){
return new Predicate<String>() {
@Override
public boolean apply(String ipAddress) {
for (SubnetUtils subnet : subnets) {
if (subnet.getInfo().isInRange(ipAddress)) {
return true;
}
}
return false;
}
};
}
Set-ipaddress=getipaddress-stofilter();
设置IPAddresssInRange=
set.filter(IP地址、filterIpsBySubnet(子网))
谓词过滤器子网(最终集子网){
返回新谓词(){
@凌驾
公共布尔应用(字符串ipAddress){
用于(子网UTILS子网:子网){
if(subnet.getInfo().isInRange(ipAddress)){
返回true;
}
}
返回false;
}
};
}
现在,如果IP在任何子网中,你都有一个很好的简单过滤器,你不必构建一个需要单元测试的数据结构
Set<String> ipAddresses = getIpAddressesToFilter();
Set<String> ipAddressesInRange =
Sets.filter(ipAddresses, filterIpsBySubnet(subnets))
Predicate<String> filterIpsBySubnet(final Set<SubnetUtils> subnets){
return new Predicate<String>() {
@Override
public boolean apply(String ipAddress) {
for (SubnetUtils subnet : subnets) {
if (subnet.getInfo().isInRange(ipAddress)) {
return true;
}
}
return false;
}
};
}