java ip 过滤_中国大陆 IP 过滤器 - Java 实现

本文介绍如何使用Java高效地判断IPv4地址是否属于中国大陆。通过CIDR格式存储和处理IP地址范围,减少内存占用并提高判断效率。具体实现包括IP到long转换、CIDR地址块计算以及构建优化的数据结构进行IP查找。
摘要由CSDN通过智能技术生成

本篇讲解如何快速判断 IPV4 地址是否在大陆境内的 IP 地址。

中国 IPV4 的地址现在大约是 3 亿 4 千万个。

github 仓库地址,源码和 ip 地址都在里面。

github.com/chenhaoxian…

目前最新是 2019 年 11 月 30 日,境内所有 ip 范围。

后面会持续更新和维护

欢迎大伙 star

最简单方法,消耗大量内存,土豪方法。

在内存中将 3 亿 4 千万 IP 全部存储到 Set 中。

如果按照 32 个字节一个 IP 来算,大约需要 10G 左右。

这种方法就不进行介绍了。

将 IP 进行拆分为 4 段。a、b、c、d 段

分段进行匹配,相较于方法一可以节省 3/4 的空间,但是需要的内存还是很大。

按照树形结构进行存储,a 匹配才进行 b 的匹配,b 匹配再进行 c 的匹配,依次匹配

只有 abcd 段完全匹配才说明 IP 在集合中

通过 CIDR 格式表示的 IPV4 地址范围进行处理

CIDR 及地址块计算

IP 地址表示法:

IP 地址 ::= {,< 主机号 >}/ 网络前缀所占位数 (斜线表示法)

CIDR 表示法给出任何一个 IP 地址就相当于给出了一个 CIDR 地址块,实现了路由的聚合

使用该方法,CIDR 格式表示的 IPV4 地址块只有几万,存储完全是够的。

通过存储 CIDR 的 IPV4 开始与结束,再将实际的 IP 进行比较是否在 CIDR 的块中,即可判断出 IP 是否在 IP 集合中

详情不进行讲解了,个人觉得使用这种方式较为麻烦,因为有着更加简便的方法

是我目前所知最好的一种判断 IP 是否在某些 IP 范围内的最好方式,如果您有更好方式,欢迎评论

我们可以知道,通过 32 位字节便可以存储一个 IP 地址,32 位字节可以转换为 long 数字,按照方法三的思路,如果使用范围的方式查找 ip 地址,那么需要的存储空间是非常小的,且效率也非常高。

那么,可以将 CIDR 表示的 IP 地址块通过存储起始 IP 数字以及其后的范围数字即可(或者存储起始数字和结束数字)

方法四核心类:

package com.uifuture.chinaipfilter;

import com.uifuture.chinaipfilter.util.FileUtils;

import org.apache.commons.net.util.SubnetUtils;

import java.util.ArrayList;

import java.util.HashMap;

import java.util.HashSet;

import java.util.List;

import java.util.Map;

import java.util.Set;

public class ChinaIPRecognizer {

private static final long RANGE_SIZE = 10000000L;

private static Map> recordMap = new HashMap<>();

private static final String CHINA_IP_PATH = "china_ip/ip_long.txt";

final static class ChinaRecord {

public long start;

public int count;

private ChinaRecord(long start, int count) {

this.start = start;

this.count = count;

}

public boolean contains(long ipValue) {

return ipValue >= start && ipValue <= start + count;

}

}

static {

List list = new ArrayList<>();

Set ipSet = FileUtils.readSetFromResourceFile(CHINA_IP_PATH);

for (String ip : ipSet) {

String[] ipS = ip.split(",");

long ipLong = Long.parseLong(ipS[0]);

int count = Integer.parseInt(ipS[1]);

ChinaRecord chinaRecord = new ChinaRecord(ipLong,count);

list.add(chinaRecord);

}

list.forEach(r -> {

int key1 = (int) (r.start / RANGE_SIZE);

int key2 = (int) ((r.start + r.count) / RANGE_SIZE);

List key1List = recordMap.getOrDefault(key1, new ArrayList<>());

key1List.add(r);

recordMap.put(key1, key1List);

if (key2 > key1) {

List key2List = recordMap.getOrDefault(key2, new ArrayList<>());

key2List.add(r);

recordMap.put(key2, key2List);

}

});

}

public static boolean isCNIP(String ip) {

if (ip == null || ip.trim().isEmpty()) {

return false;

}

if (isValidIpV4Address(ip)) {

long value = ipToLong(ip);

int key = (int) (value / RANGE_SIZE);

if (recordMap.containsKey(key)) {

List list = recordMap.get(key);

return list.stream().anyMatch((ChinaRecord r) -> r.contains(value));

}

}

return false;

}

public static boolean isValidIpV4Address(String value) {

int periods = 0;

int i;

int length = value.length();

if (length > 15) {

return false;

}

char c;

StringBuilder word = new StringBuilder();

for (i = 0; i < length; i++) {

c = value.charAt(i);

if (c == '.') {

periods++;

if (periods > 3) {

return false;

}

if (word.length() == 0) {

return false;

}

if (Integer.parseInt(word.toString()) > 255) {

return false;

}

word.delete(0, word.length());

} else if (!Character.isDigit(c)) {

return false;

} else {

if (word.length() > 2) {

return false;

}

word.append(c);

}

}

if (word.length() == 0 || Integer.parseInt(word.toString()) > 255) {

return false;

}

return periods == 3;

}

public static long ipToLong(String ipAddress) {

String[] addrArray = ipAddress.split("\\.");

long num = 0;

for (int i = 0; i < addrArray.length; i++) {

int power = 3 - i;

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

}

return num;

}

public static String longToIp(long i) {

return ((i >> 24) & 0xFF) + "." + ((i >> 16) & 0xFF) + "." + ((i >> 8) & 0xFF) + "." + (i & 0xFF);

}

public static String[] analysisCidrIp(String ip){

SubnetUtils utils = new SubnetUtils(ip);

return utils.getInfo().getAllAddresses();

}

public static void main(String[] args) {

Set ipSet = FileUtils.readSetFromResourceFile("china_ip/cidr.txt");

Set saveIp = new HashSet<>();

for (String ip : ipSet) {

String[] ips = analysisCidrIp(ip);

long min = Long.MAX_VALUE;

long max = Long.MIN_VALUE;

for (String s : ips) {

long ipL = ipToLong(s);

if(ipL>max){

max=ipL;

}

if(ipL

min=ipL;

}

}

if(max==Long.MIN_VALUE && min==Long.MAX_VALUE){

continue;

}

if(max==Long.MIN_VALUE){

max = min;

}

String save =min+","+(max-min);

saveIp.add(save);

System.out.println(save);

}

FileUtils.saveSetToResourceFile(saveIp,"china_ip/ip_long.txt");

}

}

复制代码

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值