失眠网,内容丰富有趣,生活中的好帮手!
失眠网 > 纯真IP地址数据库qqwry.dat解析

纯真IP地址数据库qqwry.dat解析

时间:2022-02-08 16:17:21

相关推荐

纯真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: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: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)|\0(1 byte)| country2(n bytes)|\0(1 byte)|...

+------------------+----------+------------------+----------+-----

+------------------+----------+------------------+----------+-----

| area1(n bytes) |\0(1 byte)| area2(n bytes) |\0(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解析即可。

如果觉得《纯真IP地址数据库qqwry.dat解析》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。