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() {@Overridepublic 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() {@Overridepublic 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;}@Overridepublic 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解析即可。

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

原文来源:https://www.jianshu.com/p/01d3c19738c2

纯真IP地址数据库qqwry.dat解析相关推荐

  1. PHP读取纯真IP地址数据库

    纯真IP地址数据库应该是国内最流行的IP地址数据库 纯真IP地址数据库(官方下载) http://www.cz88.net/fox/ipdat.shtml<?php /*------------ ...

  2. java 纯真ip 乱码_纯真ip地址数据库乱码解决方案、utf8和gbk相互转换及下载

    在使用discuz3.4论坛时,官方只提供了utf-8版本,不再提供gbk版本,因此为顺应趋势,安装使用了discuz3.4 utf-8的版本.然而,在用纯真IP库替换Discuz的默认库时却出现ip ...

  3. php qqwry.dat_php读取操作IP地址数据库文件QQWry.dat

    我们统计流量的时候需要可以获取用户ip,根据用户ip之后可以通过纯ip真数据库QQWry.dat,获取出用户IP 所在的地理位置,这样可以做出更有意义的统计信息. QQWry.dat请自行搜索下载. ...

  4. C# 调用IP库(QQWry.Dat)查询IP位置及自动升级IP库方法(附IP库下载地址及相关dll下载)

    前言 C# 用IP地址(123.125.114.144)查询位置(北京市百度公司)的东西,非常好用也非常方便,可手动升级刷新IP库,一次编码永久收益,可支持winform.asp.net等程序. 本文 ...

  5. C# 调用IP库(QQWry.Dat)查询IP位置及自动升级IP库方法(附IP库下载地址及相关dll下载)...

    前言 C# 用IP地址(123.125.114.144)查询位置(北京市百度公司)的东西,非常好用也非常方便,可手动升级刷新IP库,一次编码永久收益,可支持winform.asp.net等程序. 本文 ...

  6. C# 调用IP库(QQWry.Dat)查询IP位置及自动升级IP库方法【转】

    前言 C# 用IP地址(123.125.114.144)查询位置(北京市百度公司)的东西,非常好用也非常方便,可手动升级刷新IP库,一次编码永久收益,可支持winform.asp.net等程序. 本文 ...

  7. java读取纯真IP数据库qqwry.dat的源代码

    java读取纯真IP数据库QQwry.dat的源代码,要运行此程序必须有 到网上下载QQwry.dat,下载地址 http://www.cz88.net/down/   由于太大,我这里就不提供了. ...

  8. 纯真IP数据库(qqwry.dat)转换成最新的IP数据库格式(ipwry.dat)

    转载自:http://blog.cafeboy.org/2011/02/25/qqwry-to-ipwry/ http://blog.csdn.net/cnss/article/details/136 ...

  9. php qqwry.dat_PHP 直接从 QQWry.dat 解析IP地址的程序

    #文件名:QQWry.php /*++++++++++++++++++++++++++++++++++++ 程序名称:IP解析程序 程序功能:基于QQ的二进制数据库QQWry.Dat 程序作者:str ...

最新文章

  1. C/C++编译器mingw
  2. python turtle画熊-用Python Turtle 画可爱的熊猫
  3. 4 计算机系统的异步性,计算机操作系统的最基本特征是什么
  4. 从vivo 大规模特征存储实践中学点经验
  5. 计算机办公高级试题,高级办公软件试题及解答
  6. pdfbox 第一页加内容_Java使用PDFBox操作PDF文件获取页码、文章内容、缩略图
  7. Spring Boot 框架介绍和使用
  8. zabbix 接触这段时间的感悟
  9. 2019南昌网络赛H The Nth Item(二阶线性数列递推 + 广义斐波那契循环节 + 分段打表)题解...
  10. 64位centos 5.1(kenel版本:2.6.18-53)上安装VMware Server遇到的问题及解决方法
  11. C语言中的斐波那契数列程序
  12. 一种电力线宽带载波系统采样频偏的估计方法
  13. 福州英华职业学院计算机专业在哪个校区,福州英华职业学院五年制大专地址在哪里...
  14. 云原生数据湖以存储、计算、数据管理等能力通过信通院评测认证
  15. 华为云存储空间图库占比太大_华为手机照片太多?放这里既安全又不占内存,瞬间腾出50G空间...
  16. 我给你们做了一个金钱豹头像助手,虎年祝大家今年暴富
  17. Python笔记 Ch.13 标准库概览
  18. 博客摘录「 idea中热部署插件JRebel最新激活方式」2023年4月15日
  19. linux查看某个文件大小
  20. HT单片机笔记1-时钟配置(2022/2/20)

热门文章

  1. 屏幕滚动控件Scrollview
  2. 11g compression 新特性(1)
  3. C#二进制格式与文件相互转换
  4. Google也开始弄开源平台,好事啊
  5. Java5的 线程并发库
  6. 【Linux】开源分布式存储系统:GlusterFS
  7. 好污!杜蕾斯:一份2017年度账单和床上总结求认领...
  8. 正则表达式中模式修正符作用详解(i、g、m、s、x、e)
  9. sqlplus / as sysdba报错ORA-01031: insufficient privileges
  10. 你的云计算到底有多安全?