纯净版IP地址数据库是目前互联网上最权威、最准确、IP记录最多、网吧数据最多的IP地址数据库。采集中国电信、中国移动、中国联通、中国铁通、长城宽带等各ISP最新、准确的IP地址数据。通过大家的共同努力,我们将打造一个没有未知数据、没有错误数据的QQ IP。 IP数据库每5天更新一次。请定期更新最新的IP数据库!
格式
+————-+
|文件头| (8字节)
+————-+
|记录区| (可变长度)
+————-+
|索引区| (大小由文件头决定)
+————-+
使用java语言解析的两种思路:
使用内存映射文件方式读取,使用java的MappedByteBuffer将原始数据文件映射到MappedByteBuffer对象中,然后通过MappedByteBuffer提供的字节读取方法实现ip查找。在索引区域中使用二进制方法进行搜索
使用字节数组读取,将所有二进制数据库信息按顺序读取到一个数组中。既然数据已经格式化了,我们就可以根据索引区和记录区来计算出在数组中的位置。查询IP时,从数组中的索引区域开始,通过二分查找找到IP地址对应的国家和地区的位置,然后从数组中取出地区信息。
热升级思路:
使用可调度的单线程线程池。该线程定期检测qqwry.dat文件是否被修改。如果修改,则重新加载数据。可以使用可重入锁在加载过程中锁定资源,避免更新时的脏查询
两种解析方法的实现源码如下:
方法1(MappedByteBuffer):
包com.difeng.qqwry1;
导入java.io.File;
导入java.io.IOException;
导入java.io.RandomAccessFile;
导入java.nio.ByteOrder;
导入java.nio.MappedByteBuffer;
导入java.nio.channels.FileChannel;
导入java.util.concurrent.Executors;
导入java.util.concurrent.TimeUnit;
导入java.util.concurrent.locks.ReentrantLock;
/**
* @Description:ip位置搜索工具(使用内存映射文件读取,线程安全)
* @author:difeng
* @date: 2016 年12 月11 日
*/
公共类IPLocation {
私有静态最终int IP_RECORD_LENGTH=7;
私有静态最终字节REDIRECT_MODE_1=0x01;
私有静态最终字节REDIRECT_MODE_2=0x02;
私有MappedByteBuffer mbbFile;
私有静态长最后修改时间=0L;
公共静态布尔enableFileWatch=false;
私有静态ReentrantLock 锁=new ReentrantLock();
私人文件qqwryFile;
私有长firstIndexOffset;
私有长lastIndexOffset;
私有长totalIndexCount;
公共IPLocation(字符串文件路径)抛出异常{
this.qqwryFile=new File(文件路径);
加载();
如果(启用文件监视){
手表();
}
}
私人无效手表(){
Executors.newScheduledThreadPool(1).scheduleAtFixedRate(new Runnable() {
@覆盖
公共无效运行(){
长时间=qqwryFile.lastModified();
if (时间最后修改时间) {
最后修改时间=时间;
尝试{
加载();
} catch (异常e) {
e.printStackTrace();
}
}
}
}, 1000L, 30000L, TimeUnit.毫秒);
}
公共长read4ByteAsLong(长pos) {
mbbFile.position((int)pos);
返回0xFFFFFFFFL mbbFile.getInt();
}
公共长read3ByteAsLong(长pos){
mbbFile.position((int)pos);
返回0xFFFFFFL mbbFile.getInt();
}
@SuppressWarnings("资源")
私有void load() 抛出异常{
最后修改时间=qqwryFile.lastModified();
锁.lock();
尝试{
mbbFile=new RandomAccessFile(qqwryFile, "r")
.getChannel()
.map(FileChannel.MapMode.READ_ONLY, 0, qqwryFile.length());
mbbFile.order(ByteOrder.LITTLE_ENDIAN);
FirstIndexOffset=read4ByteAsLong(0);
最后索引偏移=read4ByteAsLong(4);
总索引计数=(最后索引偏移- 第一个索引偏移)/IP_RECORD_LENGTH + 1;
} 最后{
锁.解锁();
}
}
/**
* @Description: 转换以“.”分隔的字符串转化为long类型数字,字节顺序,如:
* ip:182.92.240.48 十六进制表示(B6.5C.F0.30)
* 转换后的IP的十六进制表示为:0xB65CF030
* @参数ipStr
* @return:long
*/
私有静态长inet_pton(String ipStr) {
if(ipStr==null){
throw new NullPointerException("ip 不能为null");
}
String[] arr=ipStr.split("\.");
长ip=(Long.parseLong(arr[0])0xFFL) 240xFF000000L;
ip |=(Long.parseLong(arr[1])0xFFL) 160xFF0000L;
ip |=(Long.parseLong(arr[2])0xFFL) 80xFF00L;
ip |=(Long.parseLong(arr[3])0xFFL);
返回ip;
}
私有长搜索(长ip) {
多头低位=0;
长高=totalIndexCount;
长中=0;
而(低=高){
中=(低+高)1;
长索引IP=read4ByteAsLong(firstIndexOffset + (mid - 1) * IP_RECORD_LENGTH);
长nextIndexIP=read4ByteAsLong(firstIndexOffset + mid * IP_RECORD_LENGTH);
if(indexIP=ip ip nextIndexIP) {
返回read3ByteAsLong(firstIndexOffset + (mid - 1) * IP_RECORD_LENGTH + 4);
} 别的{
if(ip 索引IP) {
低=中+1;
} else if(ip 索引IP) {
高=中-1;
}
}
}
返回-1;
}
私有位置readIPLocation(长偏移) {
尝试{
mbbFile.position((int)offset + 4);
位置loc=new Location();
字节重定向模式=mbbFile.get();
if (redirectMode==REDIRECT_MODE_1) {
长国家偏移=read3ByteAsLong((int)offset + 5);
mbbFile.position((int)countryOffset);
重定向模式=mbbFile.get();
if (redirectMode==REDIRECT_MODE_2) {
loc.country=readString(read3ByteAsLong(countryOffset + 1));
mbbFile.position((int)countryOffset + 4);
} 别的{
loc.country=readString(countryOffset);
}
loc.area=readArea(mbbFile.position());
} 否则if (redirectMode==REDIRECT_MODE_2) {
loc.country=readString(read3ByteAsLong((int)offset + 5));
loc.area=readArea((int)offset + 8);
} 别的{
loc.country=readString(mbbFile.position() - 1);
loc.area=readArea(mbbFile.position());
}
返回本地地址;
} catch (异常e) {
返回空值;
}
}
私有字符串readArea(int偏移量){
mbbFile.position(偏移量);
字节重定向模式=mbbFile.get();
if (重定向模式==REDIRECT_MODE_1 || 重定向模式==REDIRECT_MODE_2) {
长区域偏移=read3ByteAsLong((int)offset + 1);
if (区域偏移==0){
返回"";
} 别的{
返回readString(areaOffset);
}
} 别的{
返回读取字符串(偏移量);
}
}
私有字符串readString(长偏移量){
尝试{
mbbFile.position((int)offset);
字节[] buf=新字节[128];
整数我;
for (i=0, buf[i]=mbbFile.get(); buf[i] !=0; buf[++i]=mbbFile.get());
如果(我!=0){
return new String(buf, 0, i, "GBK");
}
} catch (IOException e) {
e.printStackTrace();
}
返回"";
}
公共位置fetchIPLocation(String ip) {
锁.lock();
尝试{
长偏移量=搜索(inet_pton(ip));
如果(偏移量!=-1){
返回读取IPLocation(偏移量);
}
} 最后{
锁.解锁();
}
返回空值;
}
}方法2(数组方法):
包com.difeng.qqwry2;
导入java.io.ByteArrayOutputStream;
导入java.io.File;
导入java.io.FileInputStream;
导入java.io.IOException;
导入java.io.UnsupportedEncodingException;
导入java.util.concurrent.Executors;
导入java.util.concurrent.TimeUnit;
导入java.util.concurrent.locks.ReentrantLock;
/**
* @Description:ip定位(使用字节数据方式读取)
* @author:difeng
* @date: 2016 年12 月13 日
*/
公共类IPLocation {
私有字节[]数据;
私有长firstIndexOffset;
私有长lastIndexOffset;
私有长totalIndexCount;
私有静态最终字节REDIRECT_MODE_1=0x01;
私有静态最终字节REDIRECT_MODE_2=0x02;
静态最终长IP_RECORD_LENGTH=7;
私有静态ReentrantLock 锁=new ReentrantLock();
私有静态长最后修改时间=0L;
公共静态布尔enableFileWatch=false;
私人文件qqwryFile;
公共IPLocation(字符串文件路径)抛出异常{
this.qqwryFile=new File(文件路径);
加载();
如果(启用文件监视){
手表();
}
}
私人无效手表(){
Executors.newScheduledThreadPool(1).scheduleAtFixedRate(new Runnable() {
@覆盖
公共无效运行(){
长时间=qqwryFile.lastModified();
if (时间最后修改时间) {
最后修改时间=时间;
尝试{
加载();
System.out.println("重新加载");
} catch (异常e) {
e.printStackTrace();
}
}
}
}, 1000L, 5000L, TimeUnit.毫秒);
}
私有void load() 抛出异常{
最后修改时间=qqwryFile.lastModified();
ByteArrayOutputStream 输出=null;
文件输入流in=null;
锁.lock();
尝试{
输出=新的ByteArrayOutputStream();
byte[] b=新字节[1024];
in=new FileInputStream(qqwryFile);
while(in.read(b) !=-1){
输出.write(b);
}
数据=out.toByteArray();
FirstIndexOffset=read4ByteAsLong(0);
最后索引偏移=read4ByteAsLong(4);
总索引计数=(最后索引偏移- 第一个索引偏移)/IP_RECORD_LENGTH + 1;
附寄();
关闭();
} 最后{
尝试{
如果(输出!=空){
关闭();
}
如果(在!=空){
附寄();
}
} catch (IOException e) {
e.printStackTrace();
}
锁.解锁();
}
}
私有长read4ByteAsLong(最终int 偏移量) {
long val=数据[偏移]0xFF;
val |=(数据[偏移量+1] 8L)0xFF00L;
val |=(数据[偏移量+2] 16L)0xFF0000L;
val |=(数据[偏移量+3] 24L)0xFF000000L;
返回值;
}
私有长read3ByteAsLong(最终int 偏移量) {
long val=数据[偏移]0xFF;
val |=(数据[偏移量+1] 8)0xFF00;
val |=(数据[偏移量+ 2] 16)0xFF0000;
返回值;
}
私有长搜索(长ip) {
多头低位=0;
长高=totalIndexCount;
长中=0;
而(低=高){
中=(低+高)1;
长索引IP=read4ByteAsLong((int)(firstIndexOffset + (mid - 1) * IP_RECORD_LENGTH));
长indexIPNext=read4ByteAsLong((int)(firstIndexOffset + mid * IP_RECORD_LENGTH));
if(indexIP=ip ip indexIPNext) {
返回read3ByteAsLong((int)(firstIndexOffset + (mid - 1) * IP_RECORD_LENGTH + 4));
} 别的{
if(ip 索引IP) {
低=中+1;
} else if (ip 索引IP) {
高=中-1;
}
}
}
返回-1;
}
公共位置fetchIPLocation(String ip) {
长数字Ip=inet_pton(ip);
锁.lock();
长偏移量=搜索(numericIp);
尝试{
如果(偏移量!=-1){
返回readIPLocation((int)offset);
}
} 最后{
锁.解锁();
}
返回空值;
}
私有位置readIPLocation(final int offset) {
最终位置loc=new Location();
尝试{
字节重定向模式=数据[偏移量+ 4];
if (redirectMode==REDIRECT_MODE_1) {
长国家偏移=read3ByteAsLong((int)offset + 5);
重定向模式=数据[(int)countryOffset];
if (redirectMode==REDIRECT_MODE_2) {
最终QQwryString 国家=readString((int)read3ByteAsLong((int)countryOffset + 1));
loc.country=Country.string;
国家偏移量=国家偏移量+4;
} 别的{
最终QQwryString 国家=readString((int)countryOffset);
loc.country=Country.string;
CountryOffset +=Country.byteCountWithEnd;
}
loc.area=readArea((int)countryOffset);
} 否则if (redirectMode==REDIRECT_MODE_2) {
loc.country=readString((int)read3ByteAsLong((int)offset + 5)).string;
loc.area=readArea((int)offset + 8);
} 别的{
最终QQwryString 国家=readString((int)offset + 4);
loc.country=Country.string;
loc.area=readArea((int)offset + 4 + Country.byteCountWithEnd);
}
返回本地地址;
} catch (异常e) {
返回空值;
}
}
私有字符串readArea(最终int偏移量){
字节重定向模式=数据[偏移];
if (重定向模式==REDIRECT_MODE_1 || 重定向模式==REDIRECT_MODE_2) {
长区域偏移=read3ByteAsLong((int)offset + 1);
if (区域偏移==0) {
返回"";
} 别的{
返回readString((int)areaOffset).string;
}
} 别的{
返回readString(offset).string;
}
}
私有QQwryString readString(int offset) {
int pos=偏移量;
最终字节[] b=新字节[128];
整数我;
for (i=0, b[i]=数据[pos++]; b[i] !=0; b[++i]=数据[pos++]);
尝试{
return new QQwryString(new String(b,0,i,"GBK"),i + 1);
} catch(UnsupportedEncodingException e) {
return new QQwryString("",0);
}
}
/**
* @Description: 转换以“.”分隔的字符串长类型数字
* @参数ipStr
* @return:long
*/
私有静态长inet_pton(String ipStr) {
if(ipStr==null){
throw new NullPointerException("ip 不能为null");
}
String[] arr=ipStr.split("\.");
长ip=(Long.parseLong(arr[0])0xFFL) 240xFF000000L;
ip |=(Long.parseLong(arr[1])0xFFL) 160xFF0000L;
ip |=(Long.parseLong(arr[2])0xFFL) 80xFF00L;
ip |=(Long.parseLong(arr[3])0xFFL);
返回ip;
}
私有类QQwryString{
公共最终字符串字符串;
公共最终int byteCountWithEnd;
公共QQwryString(最终字符串字符串,最终int byteCountWithEnd){
this.string=字符串;
this.byteCountWithEnd=byteCountWithEnd;
}
@覆盖
公共字符串toString() {
返回字符串;
}
}
}以上是主要代码。要获取所有代码,请单击“所有代码”
使用
最终IPLocation ipLocation=new IPLocation(filePath);
位置loc=ipl.fetchIPLocation("182.92.240.50");
System.out.printf("%s %s",loc.国家,loc.地区);
格式改进
由于读取原始格式的region记录时使用了重定向,所以有点麻烦。去掉之后,格式就更简单了。国家和地区分开存储,索引中分别记录国家和地区的地址。
新格式如下:
+----------+
|文件头| (8字节)
+----------+
|记录区| (可变长度)
+----------+
|索引区| (大小由文件头决定)
+----------+
文件头:
+--------------------------------+---------------------------- --- ----------+
|第一个索引位置(4 个字节)|最后一个索引位置(4 个字节)|
+--------------------------------+---------------------------- --- ----------+
录音区域:
+------------------+----------+-----------------+ ----------+-----
|国家1(n字节)| (1字节)|国家2(n 字节)| (1 字节)|.
+------------------+----------+-----------------+ ----------+-----
+------------------+----------+-----------------+ ----------+-----
|区域1(n字节)| (1字节)|区域2(n字节)| (1字节)|.
+------------------+----------+-----------------+ ----------+-----
索引区域:
+----------------+-------------------------+------ ---- ---------------+
|ip1(4 字节)|国家/地区位置(3 字节)|区域位置(3 个字节)|.
+----------------+-------------------------+------ ---- ---------------+
换算方法:
最终IPFileConvertor 转换器=new
IPFileConvertor(IPFileConvertor.class.getResource("/qqwry.dat").getPath(),"./qqwry.dat");
【深入解析纯真IP地址数据库qqwry.dat】相关文章:
2.米颠拜石
3.王羲之临池学书
8.郑板桥轶事十则
用户评论
这个工具看起来很有趣,对于研究网络安全来说应该很实用吧
有8位网友表示赞同!
我一直想了解一下我的ip地址指向哪里,这可以让我看看吗?
有15位网友表示赞同!
听说qqwry.dat数据库是免费的,是真的吗?
有6位网友表示赞同!
我之前用过类似的工具,解析速度还挺快的
有13位网友表示赞同!
这种解析方式和DNS不同吗?
有5位网友表示赞同!
这篇文章可以解释一下qqwry.dat的更新频率吗?
有12位网友表示赞同!
不知道这个数据库包含哪些国家的ip地址呢?
有12位网友表示赞同!
如果想要反推某个ip地址对应的真实位置,可以用这个工具吗?
有17位网友表示赞同!
我需要安装什么软件才能使用这种解析方法?
有10位网友表示赞同!
对研究者的帮助应该很大,可以分析网络流量的来源和分布。
有15位网友表示赞同!
这篇文章介绍了几个常用的解析工具吗?
有16位网友表示赞同!
想知道解析IP地址时需要注意些什么
有9位网友表示赞同!
qqwry.dat数据库会不会泄露用户隐私?
有8位网友表示赞同!
这种解析方法有什么缺点吗?
有19位网友表示赞同!
对于学习网络安全的同学来说,了解这方面知识很重要。
有8位网友表示赞同!
希望这篇文章能够解释清楚一些技术细节。
有6位网友表示赞同!
有没有其他可以免费获取ip地址信息的工具?
有18位网友表示赞同!
这个数据库的维护成本应该还挺高吧
有9位网友表示赞同!
看来这种解析方法只能用于非实时数据分析,对实时数据的应用效果如何?
有12位网友表示赞同!
学习一下qqwry.dat的底层原理,对理解网络技术有帮助。
有18位网友表示赞同!