zoukankan      html  css  js  c++  java
  • 纯真IP地址数据库qqwry.dat解析

    ip地址数据库,在现在互联网时代非常有用,比如大型网站的用户安全保护系统,就常常会根据ip反查的信息,甄别账号的一些不安全登录行为,比如跨区域登录问题等。ip其实关联了一些有信息,比如区域,所在运营商,一些收录全的,甚至包括具体经纬度,像百度的IP定位api就比较全。下面来介绍一下“ 纯真IP地址数据库qqwry”的格式以及解析

    以下是“ 纯真IP地址数据库qqwry”官网对其的介绍。

    纯真版IP地址数据库是当前网络上最权威、地址最精确、IP记录以及网吧数据最多的IP地址数据库。收集了包括中国电信、中国移动、中国联通、铁通、长城宽带等各 ISP 的最新准确 IP 地址数据。通过大家的共同努力打造一个没有未知数据,没有错误数据的QQ IP。IP数据库每5天更新一次,请大家定期更新最新的IP数据库!

    格式

    +———-+
    | 文件头 | (8字节)
    +———-+
    | 记录区 | (不定长)
    +———-+
    | 索引区 | (大小由文件头决定)
    +———-+

    使用java语言解析的两种思路:

    • 使用内存映射文件方式读取,使用java的MappedByteBuffer 将原数据文件映射到MappedByteBuffer对象中,然后通过MappedByteBuffer 提供的字节读取方式实现ip的查找。搜索是在索引区使用二分法

    • 使用byte数组读取,及将二进制的数据库信息全都按顺序读入到一个数组中,由于数据是有格式的,我们便可计算根据索引区和记录区在数组中的位置,当查询ip时,从数组中的索引区开始通过二分查找方式找到IP地址对应的国家和区域的位置,然后从数组中取出地区信息。

    热升级思路:

    使用一个可调度的单线程的线程池,线程定时检测qqwry.dat文件是否修改,若修改则重新将数据重新载入,载入过程可使用可重入锁ReentrantLock来锁住资源,避免在更新的过程中脏查询

    两种解析方式的实现源码如下:
    方式一(MappedByteBuffer ):

    package com.difeng.qqwry1;
    import java.io.File;
    import java.io.IOException;
    import java.io.RandomAccessFile;
    import java.nio.ByteOrder;
    import java.nio.MappedByteBuffer;
    import java.nio.channels.FileChannel;
    import java.util.concurrent.Executors;
    import java.util.concurrent.TimeUnit;
    import java.util.concurrent.locks.ReentrantLock;
    /**
     * @Description:ip定位查找工具(使用内存映射文件方式读取,线程安全)
     * @author:difeng
     * @date:2016年12月11日
     */
    public class IPLocation {
    
    	private static final int IP_RECORD_LENGTH = 7;
    
    	private static final byte REDIRECT_MODE_1 = 0x01;
    
    	private static final byte REDIRECT_MODE_2 = 0x02;
    
    	private MappedByteBuffer mbbFile;
    
    	private static Long lastModifyTime = 0L;
    
    	public static boolean enableFileWatch = false;
    
    	private static ReentrantLock lock = new ReentrantLock();
    
    	private File qqwryFile;
    
    	private long firstIndexOffset;
    
    	private long lastIndexOffset;
    
    	private long totalIndexCount;
    
    	public IPLocation(String filePath) throws Exception {
    		this.qqwryFile = new File(filePath);
    		load();
    		if (enableFileWatch) {
    			watch();
    		}
    	}
    
    	private void watch(){
    		Executors.newScheduledThreadPool(1).scheduleAtFixedRate(new Runnable() {
    			@Override
    			public void run() {
    				long time = qqwryFile.lastModified();
    				if (time > lastModifyTime) {
    					lastModifyTime = time;
    					try {
    						load();
    					} catch (Exception e) {
    						e.printStackTrace();
    					}
    				}
    			}
    		}, 1000L, 30000L, TimeUnit.MILLISECONDS);
    	}
    
    	public long read4ByteAsLong(long pos) {
    		mbbFile.position((int)pos);
    		return 0xFFFFFFFFL & mbbFile.getInt();
    	}
    
    	public long read3ByteAsLong(long pos){
    		mbbFile.position((int)pos);
    		return 0xFFFFFFL & mbbFile.getInt();
    	}
    
    
    	@SuppressWarnings("resource")
    	private void load() throws Exception {
    		lastModifyTime = qqwryFile.lastModified();
    		lock.lock();
    		try {
    			mbbFile =  new RandomAccessFile(qqwryFile, "r")
    					.getChannel()
    					.map(FileChannel.MapMode.READ_ONLY, 0, qqwryFile.length());
    			mbbFile.order(ByteOrder.LITTLE_ENDIAN);
    			firstIndexOffset = read4ByteAsLong(0);
    			lastIndexOffset = read4ByteAsLong(4);
    			totalIndexCount = (lastIndexOffset - firstIndexOffset) / IP_RECORD_LENGTH + 1;
    		} finally {
    			lock.unlock();
    		}
    	}
    	
    	/**
    	 * @Description:将“.”号分隔的字符串转换为long类型的数字,字节序例如:
    	 *   ip:182.92.240.48  16进制表示(B6.5C.F0.30)
    	 *   转换后ip的16进制表示:0xB65CF030
    	 * @param ipStr
    	 * @return:long
    	 */
    	private static long inet_pton(String ipStr) {
    		if(ipStr == null){
    			throw new NullPointerException("ip不能为空");
    		}
    		String [] arr = ipStr.split("\.");
    		long ip = (Long.parseLong(arr[0])  & 0xFFL) << 24 & 0xFF000000L;
    		ip |=  (Long.parseLong(arr[1])  & 0xFFL) << 16 & 0xFF0000L;
    		ip |=  (Long.parseLong(arr[2])  & 0xFFL) << 8 & 0xFF00L;
    		ip |=  (Long.parseLong(arr[3])  & 0xFFL);
    		return ip;
    	}
    
    	private long search(long ip) {
    		long low = 0;
    		long high = totalIndexCount;
    		long mid = 0;
    		while(low <= high) {
    			mid = (low + high) >>> 1 ;
    		    long indexIP = read4ByteAsLong(firstIndexOffset + (mid - 1) * IP_RECORD_LENGTH);
    		    long nextIndexIP =  read4ByteAsLong(firstIndexOffset + mid * IP_RECORD_LENGTH);
    		    if(indexIP <= ip && ip < nextIndexIP) {
    		    	return read3ByteAsLong(firstIndexOffset + (mid - 1) * IP_RECORD_LENGTH + 4);
    		    } else {
    		    	if(ip > indexIP) {
    		    		low = mid + 1;
    		    	} else if(ip < indexIP) {
    		    		high = mid - 1;
    		    	}
    		    }
    		}
    		return -1;
    	}
    
    	private Location readIPLocation(long offset) {
    		try {
    			mbbFile.position((int)offset + 4);
    			Location loc = new Location();
    			byte redirectMode = mbbFile.get();
    			if (redirectMode == REDIRECT_MODE_1) {
    				long countryOffset = read3ByteAsLong((int)offset + 5);
    				mbbFile.position((int)countryOffset);
    				redirectMode = mbbFile.get();
    				if (redirectMode == REDIRECT_MODE_2) {
    					loc.country = readString(read3ByteAsLong(countryOffset + 1));
    					mbbFile.position((int)countryOffset + 4);
    				} else {
    					loc.country = readString(countryOffset);
    				}
    				loc.area = readArea(mbbFile.position());
    			} else if (redirectMode == REDIRECT_MODE_2) {
    				loc.country = readString(read3ByteAsLong((int)offset + 5));
    				loc.area = readArea((int)offset + 8);
    			} else {
    				loc.country = readString(mbbFile.position() - 1);
    				loc.area = readArea(mbbFile.position());
    			}
    			return loc;
    		} catch (Exception e) {
    			return null;
    		}
    	}
    
    	private String readArea(int offset) {
    		mbbFile.position(offset);
    		byte redirectMode = mbbFile.get();
    		if (redirectMode == REDIRECT_MODE_1 || redirectMode == REDIRECT_MODE_2) {
    			long areaOffset = read3ByteAsLong((int)offset + 1);
    			if (areaOffset == 0){
    				return "";
    			} else {
    				return readString(areaOffset);
    			}
    		} else {
    			return readString(offset);
    		}
    	}
    
    	private String readString(long offset) {
    		try {
    			mbbFile.position((int)offset);
    			byte[] buf = new byte[128];
    			int i;
    			for (i = 0, buf[i] = mbbFile.get(); buf[i] != 0; buf[++i] = mbbFile.get());
    			
    			if (i != 0){
    			    return new String(buf, 0, i, "GBK");
    			}
    		} catch (IOException e) {
    			e.printStackTrace();
    		}
    		return "";
    	}
    
    	public  Location fetchIPLocation(String ip) {
    		lock.lock();
    		try {
    			long offset = search(inet_pton(ip));
    			if(offset != -1){
    				return readIPLocation(offset);
    			}
    		} finally {
    			lock.unlock();
    		}
    		return null;
    	}
    }
    
    

    方式二(数组方式):

    package com.difeng.qqwry2;
    import java.io.ByteArrayOutputStream;
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.IOException;
    import java.io.UnsupportedEncodingException;
    import java.util.concurrent.Executors;
    import java.util.concurrent.TimeUnit;
    import java.util.concurrent.locks.ReentrantLock;
    /**
     * @Description:ip定位(使用byte数据方式读取)
     * @author:difeng
     * @date:2016年12月13日
     */
    public class IPLocation {
    	
    	private  byte[] data;
    	
    	private  long firstIndexOffset;
    	
    	private  long lastIndexOffset;
    	
    	private  long totalIndexCount;
    	
    	private static final byte REDIRECT_MODE_1 = 0x01;
    	
    	private static final byte REDIRECT_MODE_2 = 0x02;
    	
    	static   final long IP_RECORD_LENGTH = 7;
    	
    	private static ReentrantLock lock = new ReentrantLock();
    	
    	private static Long lastModifyTime = 0L;
    
    	public static boolean enableFileWatch = false;
    	
    	private File qqwryFile;
    	
    	public IPLocation(String  filePath) throws Exception {
    		this.qqwryFile = new File(filePath);
    		load();
    		if(enableFileWatch){
    			watch();
    		}
    	}
    	
        private void watch() {
        	Executors.newScheduledThreadPool(1).scheduleAtFixedRate(new Runnable() {
    			@Override
    			public void run() {
    				long time = qqwryFile.lastModified();
    				if (time > lastModifyTime) {
    					lastModifyTime = time;
    					try {
    						load();
    						System.out.println("reload");
    					} catch (Exception e) {
    						e.printStackTrace();
    					}
    				}
    			}
    		}, 1000L, 5000L, TimeUnit.MILLISECONDS);
        }
        
    	private void load() throws Exception {
    		lastModifyTime = qqwryFile.lastModified();
    		ByteArrayOutputStream out = null;
    		FileInputStream in = null;
    		lock.lock();
    		try {
    			out = new ByteArrayOutputStream();
    			byte[] b = new byte[1024];
    			in = new FileInputStream(qqwryFile);
    			while(in.read(b) != -1){
    				out.write(b);
    			}
    			data = out.toByteArray();
    			firstIndexOffset = read4ByteAsLong(0);
    			lastIndexOffset = read4ByteAsLong(4);
    			totalIndexCount = (lastIndexOffset - firstIndexOffset) / IP_RECORD_LENGTH + 1;
    			in.close();
    			out.close();
    		} finally {
    			try {
    				if(out != null) {
    					out.close();
    				}
    				if(in != null) {
    					in.close();
    				}
    			} catch (IOException e) {
    				e.printStackTrace();
    			}
    			lock.unlock();
    		}
    	}
    	
    	private long read4ByteAsLong(final int  offset) {
    		long val = data[offset] & 0xFF;
    		val |= (data[offset + 1] << 8L) & 0xFF00L;
    		val |= (data[offset + 2] << 16L) & 0xFF0000L;
    		val |= (data[offset + 3] << 24L) & 0xFF000000L;
    		return val;
    	}
    
    	private long read3ByteAsLong(final int offset) {
    		long val = data[offset] & 0xFF;
    		val |= (data[offset + 1] << 8) & 0xFF00;
    		val |= (data[offset + 2] << 16) & 0xFF0000;
    		return val;
    	}
        
    	private long search(long ip) {
    		long low = 0;
    		long high = totalIndexCount;
    		long mid = 0;
    		while(low <= high){
    			mid = (low + high) >>> 1 ;
    		    long indexIP = read4ByteAsLong((int)(firstIndexOffset + (mid - 1) * IP_RECORD_LENGTH));
    	        long indexIPNext = read4ByteAsLong((int)(firstIndexOffset + mid * IP_RECORD_LENGTH));
    		    if(indexIP <= ip && ip < indexIPNext) {
    		    	return read3ByteAsLong((int)(firstIndexOffset + (mid - 1) * IP_RECORD_LENGTH + 4));
    		    } else {
    		    	if(ip > indexIP) {
    				    low = mid + 1;
    				} else if (ip < indexIP) {
    				    high = mid - 1;
    				}
    		    }
    		}
    		return -1;
    	}
    	
    	public Location fetchIPLocation(String ip) {
    		long numericIp = inet_pton(ip);
    		lock.lock();
    		long offset = search(numericIp);
    		try{
    			if(offset != -1) {
    				return readIPLocation((int)offset);
    			}
    		} finally {
    		    lock.unlock();
    		}
    		return null;
    	}
    	
    	private Location readIPLocation(final int offset) {
    		final Location loc = new Location();
    		try {
    			byte redirectMode = data[offset + 4];
    			if (redirectMode == REDIRECT_MODE_1) {
    				long countryOffset = read3ByteAsLong((int)offset + 5);
    				redirectMode = data[(int)countryOffset];
    				if (redirectMode == REDIRECT_MODE_2) {
    					final QQwryString country = readString((int)read3ByteAsLong((int)countryOffset + 1));
    					loc.country = country.string;
    					countryOffset = countryOffset + 4;
    				} else {
    					final QQwryString country = readString((int)countryOffset);
    					loc.country = country.string;
    					countryOffset += country.byteCountWithEnd;
    				}
    				loc.area = readArea((int)countryOffset);
    			} else if (redirectMode == REDIRECT_MODE_2) {
    				loc.country = readString((int)read3ByteAsLong((int)offset + 5)).string;
    				loc.area = readArea((int)offset + 8);
    			} else {
    				final QQwryString country = readString((int)offset + 4);
    				loc.country = country.string;
    				loc.area = readArea((int)offset + 4 + country.byteCountWithEnd);
    			}
    			return loc;
    		} catch (Exception e) {
    			return null;
    		}
    	}
    
    	private String readArea(final int offset) {
    		byte redirectMode = data[offset];
    		if (redirectMode == REDIRECT_MODE_1 || redirectMode == REDIRECT_MODE_2) {
    			long areaOffset = read3ByteAsLong((int)offset + 1);
    			if (areaOffset == 0) {
    				return "";
    			} else {
    				return readString((int)areaOffset).string;
    			}
    		} else {
    			return readString(offset).string;
    		}
    	}
    	
    	private QQwryString readString(int offset) {
    		int pos = offset;
    		final byte[] b = new byte[128];
    		int i;
    		for (i = 0, b[i] = data[pos++]; b[i] != 0; b[++i] = data[pos++]);
    		try{
    			   return new QQwryString(new String(b,0,i,"GBK"),i + 1);
    		} catch(UnsupportedEncodingException e) {
    			return new QQwryString("",0);
    		}
    	}
    	
    	 /**
         * @Description:“.”号分隔的字符串转换为long类型的数字
         * @param ipStr 
         * @return:long
         */
    	private static long inet_pton(String ipStr) {
    		if(ipStr == null){
    			throw new NullPointerException("ip不能为空");
    		}
    		String [] arr = ipStr.split("\.");
    		long ip = (Long.parseLong(arr[0])  & 0xFFL) << 24 & 0xFF000000L;
    		ip |=  (Long.parseLong(arr[1])  & 0xFFL) << 16 & 0xFF0000L;
    		ip |=  (Long.parseLong(arr[2])  & 0xFFL) << 8 & 0xFF00L;
    		ip |=  (Long.parseLong(arr[3])  & 0xFFL);
    		return ip;
    	}
    	
    	private class QQwryString{
    		
    		public final String string;
    		
    		public final int byteCountWithEnd;
    		
    		public QQwryString(final String string,final int byteCountWithEnd) {
    			this.string = string;
    			this.byteCountWithEnd = byteCountWithEnd;
    		}
    		
    		@Override
    		public String toString() {
    			return string;
    		}
    		
    	}
    }
    

    以上为主要代码,获取全部代码请点击全部代码

    使用

    final IPLocation ipLocation = new IPLocation(filePath);
    Location loc = ipl.fetchIPLocation("182.92.240.50");
    System.out.printf("%s %s",loc.country,loc.area);
    

    格式改进

    由于原格式中读取地区记录时采用重定向,有些繁琐。去掉之后格式更简单,国家和地区单独存放,索引里分别记录的国家和地区的地址。
    新格式如下:

    +----------+
    | 文件头 | (8字节)
    +----------+
    | 记录区 | (不定长)
    +----------+
    | 索引区 | (大小由文件头决定)
    +----------+
    文件头:
    +------------------------------+-----------------------------+
    | first index position(4 bytes)|last index position(4 bytes) |
    +------------------------------+-----------------------------+
    记录区:
    +------------------+----------+------------------+----------+-----
    | country1(n bytes)|(1 byte)| country2(n bytes)|(1 byte)|...
    +------------------+----------+------------------+----------+-----
    +------------------+----------+------------------+----------+-----
    | area1(n bytes) |(1 byte)| area2(n bytes) |(1 byte)|...
    +------------------+----------+------------------+----------+-----
    索引区:
    +------------+-------------------------+------------------------+
    |ip1(4 bytes)|country position(3 bytes)| area position(3 bytes) |...
    +------------+-------------------------+------------------------+
    转换方法:

    final IPFileConvertor convertor = new 
    IPFileConvertor(IPFileConvertor.class.getResource("/qqwry.dat").getPath(),"./qqwry.dat");
    convertor.convert();
    新格式使用方法和之前的一致,使用com.difeng.convert包下的解析类IPLocation解析即可。
    

    相关连接:
    qqwry下载: qqwry
    全球ip地址库(收费):IPLocation

  • 相关阅读:
    2、容器初探
    3、二叉树:先序,中序,后序循环遍历详解
    Hebbian Learning Rule
    论文笔记 Weakly-Supervised Spatial Context Networks
    在Caffe添加Python layer详细步骤
    论文笔记 Learning to Compare Image Patches via Convolutional Neural Networks
    Deconvolution 反卷积理解
    论文笔记 Feature Pyramid Networks for Object Detection
    Caffe2 初识
    论文笔记 Densely Connected Convolutional Networks
  • 原文地址:https://www.cnblogs.com/difeng/p/7172081.html
Copyright © 2011-2022 走看看