使用开源ip2region的IPV4库实现Ip分析

13 篇文章 0 订阅
3 篇文章 0 订阅

IPMatcher类

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.Inet4Address;
import java.net.UnknownHostException;
import java.nio.file.StandardCopyOption;
import java.util.Objects;
import org.lionsoul.ip2region.DataBlock;
import org.lionsoul.ip2region.DbConfig;
import org.lionsoul.ip2region.DbMakerConfigException;
import org.lionsoul.ip2region.DbSearcher;
import org.lionsoul.ip2region.Util;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.apobates.forum.utils.lang.Result;
/**
 * IP地址匹配器,使用开源项目(ip2region)
 * {@link https://gitee.com/lionsoul/ip2region}
 * 
 * @author xiaofanku
 * @since 20191206
 */
public final class IPMatcher {
	/**
	 * 失败时的返回的占位符
	 */
	public final static String MARK="-";
	private static IPMatcher instance=null;
	private final DbSearcher searcher;
	private final static Logger logger = LoggerFactory.getLogger(IPMatcher.class);
	static {
		try{
			instance = new IPMatcher();
		}catch(Exception e){
			if(logger.isDebugEnabled()){
				logger.debug("[IPM]instace exception: "+e.getMessage());
			}
		}
	}
	
	public static IPMatcher getInstance() {
		return instance;
	}
	
	private IPMatcher() throws DbMakerConfigException, IOException{
		// db
		InputStream istream = this.getClass().getClassLoader().getResourceAsStream("META-INF/ip2region.db");
		File targetFile = File.createTempFile("ip2region", ".db");
		java.nio.file.Files.copy(istream, targetFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
		if (targetFile.exists() == false) {
			throw new IllegalStateException("[IPM]database file Lost");
		}
		DbConfig config = new DbConfig();
		this.searcher = new DbSearcher(config, targetFile.getPath());
	}
	
	/**
	 * 查找方法
	 * 
	 * @param ip ip地址
	 * @return
	 * @throws IllegalStateException    对查找过程产生异常的包装
	 * @throws IllegalArgumentException ip地址检查失败时抛出
	 * @throws NullPointerException     若参数是null时抛出
	 */
	private String getCityInfo(String ip)throws IllegalStateException, IllegalArgumentException, NullPointerException {
		Objects.requireNonNull(ip);
		try {
			if (Util.isIpAddress(ip) == false) {
				throw new IllegalArgumentException("参数不符合合法的地址");
			}
			DataBlock dataBlock = searcher.memorySearch(ip);//.btreeSearch(ip);
			return dataBlock.getRegion();
		} catch (NullPointerException | IOException e) {
			if(logger.isDebugEnabled()){
				logger.debug("[IPM]get city info exception: "+e.getMessage());
			}
			throw new IllegalStateException(e);
		}
	}
	
	/**
	 * 返回字符串的匹配结果
	 * 
	 * @param ipAddr ip地址
	 * @return
	 */
	public Result<String> matchToString(String ipAddr){
		if(isLoopbackIp(ipAddr)){
			return Result.empty();
		}
		try{
			String data = getCityInfo(ipAddr);
			return Result.ofNullable(data);
		}catch(IllegalStateException | IllegalArgumentException | NullPointerException e){
			if(logger.isDebugEnabled()){
				logger.debug("[IPM]matchToString exception: "+e.getMessage());
			}
			return Result.failure(e.getMessage());
		}
	}
	
	/**
	 * 返回IpMatchResult的匹配结果
	 * 
	 * @param ipAddr ip地址
	 * @return
	 */
	public Result<IpMatchResult> matchToResult(String ipAddr){
		if(isLoopbackIp(ipAddr)){
			return Result.empty();
		}
		try{
			String[] data=getCityInfo(ipAddr).split("\\|"); //国家|大区|省份|城市|运营商
			return Result.success(new IpMatchResult(ipAddr, data[4], data[2], data[3], MARK));
		}catch(IllegalStateException | IllegalArgumentException | NullPointerException | ArrayIndexOutOfBoundsException e){
			if(logger.isDebugEnabled()){
				logger.debug("[IPM]matchToResult exception: "+e.getMessage());
			}
			return Result.failure(e.getMessage());
		}
	}
	
	/**
	 * 是否是本地的环回地址
	 * 
	 * @param ipAddr ip地址
	 * @return true是,false不是
	 */
	public static boolean isLoopbackIp(String ipAddr){
		try{
			return Inet4Address.getByName(ipAddr).isLoopbackAddress();
		}catch(UnknownHostException e){
			return false;
		}
	}
	
	public static class IpMatchResult{
		private final String ipAddr;
		//供应商:联通|电信
		private final String isp;
		//地域:省
		private final String province;
		//地域:市
		private final String city;
		//地域:区
		private final String district;
		
		public IpMatchResult(String ipAddr, String isp, String province, String city, String district) {
			this.ipAddr = ipAddr;
			this.isp = isp;
			this.province = province;
			this.city = city;
			this.district = district;
		}
		/**
		 * 返回ISP供应商,例:联通
		 * @return
		 */
		public String getIsp() {
			return isp;
		}
		/**
		 * 返回省份,例:山东省
		 * @return
		 */
		public String getProvince() {
			return province;
		}
		/**
		 * 返回市,例:烟台市
		 * @return
		 */
		public String getCity() {
			return city;
		}
		/**
		 * 返回区或县
		 * @return
		 */
		public String getDistrict() {
			return district;
		}
		
		public String getIpAddr() {
			return ipAddr;
		}
		
		@Override
		public String toString() {
			return String.format("{\"IP\":\"%s\", \"ISP供应商\":\"%s\", \"省\":\"%s\", \"市\": \"%s\"}", getIpAddr(), getIsp(), getProvince(), getCity());
		}
	}
}

主类修正后的源码@20191213

测试类

import java.util.Arrays;
import java.util.List;
import org.junit.Ignore;
import org.junit.Test;
import com.apobates.forum.utils.ip.IPMatcher;
import com.apobates.forum.utils.ip.IPMatcher.IpMatchResult;

public class IPMatcherTest {
	@Test
	public void testIPM(){
		String ip = "27.216.186.85";//27.216.186.85
		IpMatchResult mr = IPMatcher.getInstance().matchToResult(ip).getOrElse(()->null);
		if(mr != null){
			//省: 山东省, 市: 烟台市, ISP: 联通
			System.out.println("省: "+mr.getProvince()+", 市: "+mr.getCity()+", ISP: "+mr.getIsp());
		}
	}
	
	@Test
	public void testIPMS(){
		String ip = "27.216.186.85";//27.216.186.85
		String str = IPMatcher.getInstance().matchToString(ip).getOrElse("*");
		System.out.println("结果: "+str); //中国|0|山东省|烟台市|联通
		
	}
	@Test 
	public void batchIPM(){
		List<String> ips = Arrays.asList("127.0.0.111", "127.0.0.1", "192.168.0.18", "192.168.0.32", "192.168.0.24", "192.168.0.76", 
				"192.168.0.23", "192.168.0.69", "0:0:0:0:0:0:0:1", "122.5.29.130","27.217.117.215", "123.130.141.13", "27.217.119.35", 
				"223.104.4.120", "106.39.189.28", "117.136.46.70", "218.90.102.67", "117.136.45.127", "39.73.162.10", "121.235.97.60", 
				"123.130.50.82", "123.130.163.74", "39.73.124.229", "39.73.125.148", "58.247.212.140", "223.104.13.156", "223.104.3.194", 
				"27.216.162.51", "14.215.176.6", "14.215.176.12", "123.232.37.131", "39.86.234.19", "123.233.124.229", "100.116.251.34", 
				"100.116.251.112", "100.116.251.72", "100.116.251.52", "100.116.251.37", "100.116.251.43", "122.5.29.130", "100.116.251.85", 
				"100.116.251.57", "122.5.29.130", "100.116.251.58", "100.116.236.232", "100.116.236.224", "100.116.236.230", "100.116.236.234", 
				"100.116.251.238", "100.116.251.230", "100.116.251.208", "100.116.251.218", "100.116.236.231", "100.116.236.235", 
				"100.116.236.238", "100.116.251.219", "100.116.251.235", "100.116.251.234", "100.116.251.222", "100.116.251.233");
		final IPMatcher ipm = IPMatcher.getInstance();
		ips.stream().parallel().map(ip->ipm.matchToResult(ip)).forEach(System.out::println);
	}
}

说明

1)为什么是单例

这个很明现因为要载入ip数据库. 本人比较菜,可能这个单例写法不一定完美. 不要像有些文档说的写一个静态工具方法。如果调用频繁每次都载入ip数据库是很浪费时间的。另外在实际项目中必需使用内存查找.

2) Result是什么

是一个类Optional的工具类,提供三种情况: 空,异常,正常三种情况。代码在哪呢?请看聊聊WEB项目中的图片的G1中找到下载地址

3)最后

ip2region.db放在哪了?

在这里插入图片描述

这个文件我也是在官方下载的.

项目的依赖及版本

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<maven.compiler.source>1.8</maven.compiler.source>
		<maven.compiler.target>1.8</maven.compiler.target>
		<ip2region.version>1.7.2</ip2region.version>
		<slf4j.version>1.7.25</slf4j.version>
	</properties>

若有更完美的写法也请您与小可分享一下,在此先谢过了。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值