最新一代文件结构 超高性能解析IP数据库 qqzeng-ip.dat
高性能IP数据库格式 qqzeng-ip.dat
编码:UTF8 字节序:Little-Endian
返回多个字段信息(如:亚洲|中国|香港|九龙|油尖旺|新世界电讯|810200|Hong Kong|HK|114.17495|22.327115)
------------------------ 文件结构 ---------------------------
//文件头 16字节(4-4-4-4)
[索引区第一条流位置][索引区最后一条流位置][前缀区第一条的流位置][前缀区最后一条的流位置]
//内容区 长度无限制
[地区信息][地区信息]……唯一不重复
//索引区 12字节(4-4-3-1)
[起始IP][结束IP][地区流位置][流长度]
//前缀区 9字节(1-4-4)
[0-255][索引区start索引][索引区end索引]
------------------------ 文件结构 ---------------------------
优势:索引区分为[起始IP][结束IP][地区偏移][长度],减少多级偏移跳转步骤和长度的解析,提高效率;
根据ip第一位字节作为前缀,解析出以这个数字为前缀的第一个索引和最后一个索引,缩小查询区间,
然后在这区间再用二分查找快速查找到对应区间,效率提高几个等级
压缩:原版txt为15M,生成这种dat结构为2.45M
性能:每秒解析300多万
对比:相比其他dat更简洁更高效
创建:qqzeng-ip 于 2015-08-01
代码:https://github.com/zengzhan/qqzeng-ip

public class IPSearch
{
private Dictionary<uint, PrefixIndex> prefixDict;
private byte[] indexBuffer;
private byte[] data;
long firstStartIpOffset;//索引区第一条流位置
long lastStartIpOffset;//索引区最后一条流位置
long prefixStartOffset;//前缀区第一条的流位置
long prefixEndOffset;//前缀区最后一条的流位置
long ipCount; //ip段数量
long prefixCount; //前缀数量 /// <summary>
/// 初始化二进制dat数据
/// </summary>
/// <param name="dataPath"></param>
public IPSearch(string dataPath)
{
using (FileStream fs = new FileStream(dataPath, FileMode.Open, FileAccess.Read, FileShare.Read))
{
data = new byte[fs.Length];
fs.Read(data, , data.Length);
} firstStartIpOffset = BytesToLong(data[], data[], data[], data[]);
lastStartIpOffset = BytesToLong(data[], data[], data[], data[]);
prefixStartOffset = BytesToLong(data[], data[], data[], data[]);
prefixEndOffset = BytesToLong(data[], data[], data[], data[]); //prefixCount 不固定为256 方便以后自由定制 国内版 国外版 全球版 或者某部分 都可以 ipCount = (lastStartIpOffset - firstStartIpOffset) / + ; //索引区块每组 12字节
prefixCount = (prefixEndOffset - prefixStartOffset) / + ; //前缀区块每组 9字节 //初始化前缀对应索引区区间
indexBuffer = new byte[prefixCount * ];
Array.Copy(data, prefixStartOffset, indexBuffer, , prefixCount * );
prefixDict = new Dictionary<uint, PrefixIndex>();
for (var k = ; k < prefixCount; k++)
{
int i = k * ;
uint prefix = (uint)indexBuffer[i];
long start_index = BytesToLong(indexBuffer[i + ], indexBuffer[i + ], indexBuffer[i + ], indexBuffer[i + ]);
long end_index = BytesToLong(indexBuffer[i + ], indexBuffer[i + ], indexBuffer[i + ], indexBuffer[i + ]);
prefixDict.Add(prefix, new PrefixIndex() { prefix = prefix, start_index = start_index, end_index = end_index });
} } public static uint IpToInt(string ip,out uint prefix)
{
byte[] bytes = IPAddress.Parse(ip).GetAddressBytes();
prefix = (uint)bytes[];
return (uint)bytes[] + (((uint)bytes[]) << ) + (((uint)bytes[]) << ) + (((uint)bytes[]) << );
} public static string IntToIP(uint ip_Int)
{
return new IPAddress(ip_Int).ToString();
} /// <summary>
/// 根据ip查询多维字段信息
/// </summary>
/// <param name="ip">ip地址(123.4.5.6)</param>
/// <returns>亚洲|中国|香港|九龙|油尖旺|新世界电讯|810200|Hong Kong|HK|114.17495|22.327115</returns>
public string Query(string ip)
{
uint ip_prefix_value;
uint intIP = IpToInt(ip,out ip_prefix_value);
uint high = ;
uint low = ;
uint startIp = ;
uint endIp = ;
uint local_offset = ;
uint local_length = ; if (prefixDict.ContainsKey(ip_prefix_value))
{
low = (uint)prefixDict[ip_prefix_value].start_index;
high = (uint)prefixDict[ip_prefix_value].end_index;
}
else
{
return "";
} uint my_index = low == high? low : BinarySearch(low, high, intIP); GetIndex(my_index, out startIp, out endIp, out local_offset, out local_length); if ((startIp <= intIP) && (endIp >= intIP))
{
return GetLocal(local_offset, local_length);
}
else
{
return "";
} }
/// <summary>
/// 二分逼近算法
/// </summary>
public uint BinarySearch(uint low, uint high, uint k)
{
uint M = ;
while (low <= high )
{
uint mid = (low + high) / ; uint endipNum = GetEndIp(mid);
if (endipNum >= k)
{ M = mid;
if (mid == )
{
break; //防止溢出
}
high = mid - ;
}
else
low = mid + ;
}
return M;
}
/// <summary>
/// 在索引区解析
/// </summary>
/// <param name="left">ip第left个索引</param>
/// <param name="startip">返回开始ip的数值</param>
/// <param name="endip">返回结束ip的数值</param>
/// <param name="local_offset">返回地址信息的流位置</param>
/// <param name="local_length">返回地址信息的流长度</param>
private void GetIndex(uint left, out uint startip, out uint endip, out uint local_offset, out uint local_length)
{
long left_offset = firstStartIpOffset + (left * );
startip = BytesToLong(data[left_offset], data[ + left_offset], data[ + left_offset],data[ + left_offset]);
endip = BytesToLong(data[+left_offset], data[ + left_offset], data[ + left_offset], data[ + left_offset]);
local_offset = (uint)data[ + left_offset] + (((uint)data[ + left_offset]) << ) + (((uint)data[ + left_offset]) << );
local_length = (uint)data[ + left_offset];
}
/// <summary>
/// 只获取结束ip的数值
/// </summary>
/// <param name="left">索引区第left个索引</param>
/// <returns>返回结束ip的数值</returns>
private uint GetEndIp(uint left)
{
long left_offset = firstStartIpOffset + (left * );
return BytesToLong(data[ + left_offset], data[ + left_offset], data[ + left_offset], data[ + left_offset]); } /// <summary>
/// 返回地址信息
/// </summary>
/// <param name="local_offset">地址信息的流位置</param>
/// <param name="local_length">地址信息的流长度</param>
/// <returns></returns>
private string GetLocal(uint local_offset, uint local_length)
{
byte[] buf = new byte[local_length];
Array.Copy(data, local_offset, buf, , local_length);
return Encoding.UTF8.GetString(buf, , (int)local_length); } /// <summary>
/// 字节转整形 小节序
/// </summary>
/// <param name="a"></param>
/// <param name="b"></param>
/// <param name="c"></param>
/// <param name="d"></param>
/// <returns></returns>
private uint BytesToLong(byte a, byte b, byte c, byte d)
{
return ((uint)a << ) | ((uint)b << ) | ((uint)c << ) | ((uint)d << );
}
} /*
(调用例子):
IPSearch finder = new IPSearch("qqzeng-ip.dat");
string result = finder.Query("1.2.3.4");
--> result="亚洲|中国|香港|九龙|油尖旺|新世界电讯|810200|Hong Kong|HK|114.17495|22.327115"
*/ public class PrefixIndex
{
public uint prefix { get; set; }
public long start_index { get; set; }
public long end_index { get; set; }
}
最新一代文件结构 超高性能解析IP数据库 qqzeng-ip.dat的更多相关文章
- 纯真IP数据库(qqwry.dat)转换成最新的IP数据库格式(ipwry.dat)
纯真IP数据库(qqwry.dat)转换成最新的IP数据库格式(ipwry.dat) 转载自:http://blog.cafeboy.org/2011/02/25/qqwry-to-ipwry/ ht ...
- python3通过纯真IP数据库查询IP归属地信息
在网上看到的别人写的python2的代码,修改成了python3. 把纯真IP数据库文件qqwry.dat放到czip.py同一目录下. #! /usr/bin/env python # -*- co ...
- 【VB.NET】利用纯真IP数据库查询IP地址及信息
几年前从某个博客抄来的,已经忘记原地址了,如果需要C#版的,可以在博客园搜到吧.我因为自己用,所以转换为了VBNET代码,而且也放置了很久,今天无意间翻出来,就分享给大家吧. 首先,先下载 纯真数据库 ...
- 优化读取纯真IP数据库QQWry.dat获取地区信息
改自HeDaode 2007-12-28的代码 将之改为从硬盘读取后文件后,将MemoryStream放到内存中,提高后续查询速度 ///<summary> /// 提供从纯真IP数据库搜 ...
- IP数据库
免费的IP数据库,qqwry.dat文件:通过读文件来获取ip地址的地区信息: QQWry.Dat的格式如下: +----------+| 文件头 | (8字节)+----------+| 记录区 | ...
- 最新IP数据库 存储优化 查询性能优化 每秒解析上千万
高性能IP数据库格式详解 每秒解析1000多万ip qqzeng-ip-ultimate.dat 3.0版 编码:UTF8 字节序:Little-Endian 返回规范字段(如:亚洲|中国| ...
- 纯真IP数据库解析Delphi D10.1下正常使用
直接一个单元,代码分享出来. unit Net.IPLocation; interface uses System.Classes, System.SysUtils, Winapi.WinSock ...
- OpenStack最新版本Folsom架构解析
OpenStack最新版本Folsom架构解析摘要:OpenStack的第6版,版本代号为Folsom的最新版于今年九月底正式发布,Folsom将支持下一代软件定义网络(SDN)作为其核心组成部分.F ...
- 数据采集:完美下载淘宝Ip数据库 简单的程序节省60元人民币而不必购买数据库
曾经做网站类型的程序时,经常需要收集客户端的访问数据,然后加以分析.这需要一个Ip数据库,数据表中显示Ip所在的省份市区等信息.网络上有流传的Ip纯真数据库,一些公开的Web服务也可以查询Ip地址信息 ...
随机推荐
- js的三种继承方式及其优缺点
[转] 第一种,prototype的方式: //父类 function person(){ this.hair = 'black'; this.eye = 'black'; this.skin = ' ...
- Python实战:下载鬼灵报告有声小说
在家无聊,想看看小说,不过看的眼睛痛,就想着下个有声小说来听听.但风上找到的都是要一集一集下,还得重命名,122集啊,点到什么时候. 写个批处理下载的脚本.记录下过程. 一.老套路了,找到下载URL. ...
- Python In Action:三、再来一个扩展例子,保证不难
在窗口显示一张图片,代码如下: import wx class Frame(wx.Frame): """Frame class that displays an imag ...
- android获取本地图片并显示图片
import java.io.FileNotFoundException; import android.content.ContentResolver; import android.content ...
- [WinApi] C#获取其他窗口文本框内容(转)
声明部分: const int WM_GETTEXT = 0x000D; const int WM_GETTEXTLENGTH = 0x000E; [DllImport("user32.dl ...
- iOS 常用的向上,向下取整, 四舍五入函数
向上取整:ceil(x),返回不小于x的最小整数; 向下取整:floor(x),返回不大于x的最大整数; 四舍五入:round(x) 截尾取整函数:trunc(x)
- 安卓中級教程(7):annotation中的 public @interface的用法
package com.example.ele_me.util; import java.lang.annotation.Retention; import java.lang.annotation. ...
- python学习道路(day4note)(函数,形参实参位置参数匿名参数,匿名函数,高阶函数,镶嵌函数)
1.函数 2种编程方法 关键词面向对象:华山派 --->> 类----->class面向过程:少林派 -->> 过程--->def 函数式编程:逍遥派 --> ...
- select case巧用 oracle
在做报表的时候,经常会用到分段统计数据,这时候,case就比较有用了: 小例子: SELECT sh.dc_code, g.bg_code, TO_CHAR ( ...
- 给Source Insight做个外挂系列之三--构建外挂软件的定制代码框架
上一篇文章介绍了“TabSiPlus”是如何进行代码注入的,本篇将介绍如何构建一个外挂软件最重要的部分,也就是为其扩展功能的定制代码.本文前面提到过,由于windows进程管理的限制,扩展代码必须以动 ...