Compare commits
47 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
74b3045339 | |
|
|
7cb8bc4b9d | |
|
|
b65d8206ba | |
|
|
39555fc266 | |
|
|
68540b4db4 | |
|
|
217a7dab9e | |
|
|
07ea5994b8 | |
|
|
02391e2057 | |
|
|
5f29715ec2 | |
|
|
4d0ce55008 | |
|
|
29bacb0c8a | |
|
|
a49fae2ce8 | |
|
|
1f0b07d42b | |
|
|
b0aecd7046 | |
|
|
b48dbe5248 | |
|
|
4ffb1334a1 | |
|
|
0f39924b08 | |
|
|
7f1216eb6f | |
|
|
6eec7a2c42 | |
|
|
499bc1887e | |
|
|
1ad6274118 | |
|
|
769ab315b1 | |
|
|
979e9bdda7 | |
|
|
e4f54054c4 | |
|
|
af9c8794ff | |
|
|
f93b8d2dfb | |
|
|
546be60b0a | |
|
|
eae269f37d | |
|
|
90682e06eb | |
|
|
7d4a36d9bc | |
|
|
fe4057d8aa | |
|
|
bd3ffadabf | |
|
|
b7376d5868 | |
|
|
387f5a2097 | |
|
|
9de920ef2b | |
|
|
639c852534 | |
|
|
3c8cfa1282 | |
|
|
a67936d835 | |
|
|
9bdf030fe0 | |
|
|
7a2b9943b5 | |
|
|
c0955fedff | |
|
|
21bb060839 | |
|
|
b0f7bff2de | |
|
|
643e6a2463 | |
|
|
ec395be524 | |
|
|
7dcec80080 | |
|
|
bedaca5b70 |
|
|
@ -4,11 +4,21 @@
|
|||
*.pyc
|
||||
*~
|
||||
META-INF/
|
||||
|
||||
# Binary Files #
|
||||
*.jar
|
||||
!dbMaker-*.jar
|
||||
|
||||
# Package Files #
|
||||
.settings/
|
||||
.classpath
|
||||
.project
|
||||
|
||||
# vim swp file #
|
||||
*.swp
|
||||
|
||||
# binding
|
||||
/binding/java/classes/
|
||||
/binding/java/doc/
|
||||
/binding/java/target/
|
||||
/binding/java/*.jar
|
||||
|
|
|
|||
31
CHANGES.md
31
CHANGES.md
|
|
@ -1,3 +1,34 @@
|
|||
### 1.2.4
|
||||
1, 花了近两周的时间重写了数据的升级算法,再次提高准确率,升级过程如下(此处应该有掌声):
|
||||
|
||||
```shell
|
||||
1. ip预分段,利于分布式同步升级
|
||||
2. 分段同步升级,目前使用四个机器同时升级
|
||||
3,自动验证数据查缺补全
|
||||
4,数据合并
|
||||
5,数据格式标准化,目前2个工作:
|
||||
1),ipip.net的数据和淘宝数据保持区域名称统一,ipip.net的数据不带“省”和“市”关键字。
|
||||
2),香港澳门台湾等国家信息修复(淘宝原始数据有问题),reported at http://git.oschina.net/lionsoul/ip2region/issues/21
|
||||
6,重复ip段的合并得到data目录下的ip.merge.txt
|
||||
7,生产data目录下的ip2region.db二进制数据库文件
|
||||
```
|
||||
|
||||
2,修复原始数据关于“香港,台湾,澳门”国家信息错误的bug,reported at http://git.oschina.net/lionsoul/ip2region/issues/21
|
||||
3,将数据升级至:2017/03/15同步版本
|
||||
4,增加c_mmap查询客户端,C客户端查询文件读取使用内存映射加速,感谢[Leo Ma](http://git.oschina.net/begeekmyfriend)的贡献
|
||||
|
||||
备注:部分阿里云数据有问题,缺失的部分使用免费开放的ipip.net的数据代替了。
|
||||
|
||||
### 1.2.3
|
||||
1. 优化升级算法,进行更深度的分裂,可以使准确率再度提高(同时数据的更新也花费了2天多的时间)。
|
||||
2. 数据升级至2016/12/14版本的数据(包括原始数据和binary数据库的数据)。
|
||||
|
||||
### 1.2.2
|
||||
|
||||
1. ip数据文件更新至2016/11/02版本
|
||||
2. 增加ip2region.db数据库文件的java生成工具,请参考README获取使用方式
|
||||
3. 新增golang查询客户端的实现,感谢 [@mohong122](https://github.com/mohong122) 的关注和贡献
|
||||
|
||||
### 1.2
|
||||
|
||||
1. 新增分列式升级算法,大大的增加了数据的准确率,基本避免之前各大网友反馈的些许ip定位错误!
|
||||
|
|
|
|||
|
|
@ -201,9 +201,9 @@
|
|||
limitations under the License.
|
||||
|
||||
==========================================================================
|
||||
The following license applies to the JQuery JavaScript library
|
||||
The following license applies to the ip2region library
|
||||
--------------------------------------------------------------------------
|
||||
Copyright (c) 2010 John Resig, http://jquery.com/
|
||||
Copyright (c) 2015 Lionsoul<chenxin619315@gmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
|
|
@ -223,4 +223,3 @@ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
|
|
|
|||
109
README.md
109
README.md
|
|
@ -1,27 +1,28 @@
|
|||
ip2region - 最自由的ip地址查询库,ip到地区的映射库,提供Binary,B树和纯内存三种查询算法,妈妈再也不用担心我的ip地址定位。
|
||||
|
||||
**1. 99.9%准确率,定时更新:**
|
||||
### 1. 99.9%准确率,定时更新:
|
||||
|
||||
数据聚合了一些知名ip到地名查询提供商的数据,这些是他们官方的的准确率,经测试着实比纯真啥的准确多了。<br />
|
||||
每次聚合一下数据需要1-2天,会不定时更新。
|
||||
|
||||
**2. 标准化的数据格式:**
|
||||
### 2. 标准化的数据格式:
|
||||
|
||||
每条ip数据段都固定了格式:_城市Id|国家|区域|省份|城市|ISP_
|
||||
|
||||
只有中国的数据精确到了城市,其他国家只能定位到国家,后前的选项全部是0,已经包含了全部你能查到的大大小小的国家。
|
||||
(请忽略前面的城市Id,个人项目需求)
|
||||
|
||||
**3. 体积小:**
|
||||
### 3. 体积小:
|
||||
|
||||
生成的数据库文件ip2region.db只有1.5M(1.2版本前是3.5M)
|
||||
|
||||
**4. 多查询客户端的支持,0.0x毫秒级别的查询**
|
||||
### 4. 多查询客户端的支持,0.0x毫秒级别的查询
|
||||
|
||||
已经集成的客户端有:java, php, c, python,nodejs,php扩展(支持linux, php5, php7版本已支持)。
|
||||
已经集成的客户端有:java, C#, php, c, python,nodejs,php扩展(支持linux, php5, php7版本已支持),golang。
|
||||
|
||||
提供了两种查询算法,响应时间如下:
|
||||
客户端/binary算法/b-tree算法/Memory算法:
|
||||
c#/0.x毫秒/0.x毫秒/0.x毫秒
|
||||
java/0.x毫秒/0.x毫秒/0.1x毫秒 (使用RandomAccessFile)
|
||||
php/0.x毫秒/0.1x毫秒/0.1x毫秒
|
||||
c/0.0x毫秒/0.0x毫秒/0.00x毫秒(b-tree算法基本稳定在0.02x毫秒级别)
|
||||
|
|
@ -29,38 +30,92 @@ ip2region - 最自由的ip地址查询库,ip到地区的映射库,提供Bina
|
|||
|
||||
任何客户端b-tree都比binary算法快,当然Memory算法固然是最快的!
|
||||
|
||||
**5. 测试程序:**
|
||||
java:
|
||||
maven仓库地址:
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>org.lionsoul</groupId>
|
||||
<artifactId>ip2region</artifactId>
|
||||
<version>1.4</version>
|
||||
</dependency>
|
||||
```
|
||||
nuget安装命令
|
||||
```shell
|
||||
Install-Package IP2Region
|
||||
```
|
||||
|
||||
cd binding/java
|
||||
ant all
|
||||
java -jar ip2region-{version}.jar ./data/ip2region.db
|
||||
### 5. 测试程序:
|
||||
c#:
|
||||
```shell
|
||||
cd IP2Region_ConsoleTest
|
||||
dotnet run
|
||||
example result:
|
||||
请输入IP地址:
|
||||
36.149.160.55
|
||||
```
|
||||
|
||||
java:
|
||||
```shell
|
||||
cd binding/java
|
||||
ant all
|
||||
java -jar ip2region-{version}.jar ./data/ip2region.db
|
||||
```
|
||||
|
||||
php:
|
||||
|
||||
php binding/php/testSearcher ./data/ip2region.db
|
||||
```shell
|
||||
php binding/php/testSearcher.php ./data/ip2region.db
|
||||
```
|
||||
|
||||
c:
|
||||
|
||||
cd binding/c/
|
||||
gcc -g -O2 testSearcher.c ip2region.c
|
||||
./a.out ../../data/ip2region.db
|
||||
```shell
|
||||
cd binding/c/
|
||||
gcc -g -O2 testSearcher.c ip2region.c
|
||||
./a.out ../../data/ip2region.db
|
||||
```
|
||||
|
||||
python:
|
||||
|
||||
python binding/python/testSearcher ./data/ip2region.db
|
||||
```shell
|
||||
python binding/python/testSearcher.py ./data/ip2region.db
|
||||
```
|
||||
|
||||
均会看到如下界面:
|
||||
|
||||
initializing B-tree ...
|
||||
+----------------------------------+
|
||||
| ip2region test script |
|
||||
| Author: chenxin619315@gmail.com |
|
||||
| Type 'quit' to exit program |
|
||||
+----------------------------------+
|
||||
p2region>> 101.105.35.57
|
||||
2163|中国|华南|广东省|深圳市|鹏博士 in 0.02295 millseconds
|
||||
```shell
|
||||
initializing B-tree ...
|
||||
+----------------------------------+
|
||||
| ip2region test script |
|
||||
| Author: chenxin619315@gmail.com |
|
||||
| Type 'quit' to exit program |
|
||||
+----------------------------------+
|
||||
p2region>> 101.105.35.57
|
||||
2163|中国|华南|广东省|深圳市|鹏博士 in 0.02295 millseconds
|
||||
```
|
||||
|
||||
输入ip地址开始测试,第一次会稍微有点慢,在运行命令后面接入binary,memory来尝试其他算法,建议使用b-tree算法,速度和并发需求的可以使用memory算法。
|
||||
|
||||
具体集成请参考不同客户端的测试源码。
|
||||
|
||||
|
||||
### 6. 如何生成ip2region.db文件
|
||||
|
||||
从ip2region 1.2.2版本开始里面提交了一个dbMaker-{version}.jar的可以执行jar文件,用它来完成这个工作:
|
||||
* 1, 确保你安装好了java环境(不玩Java的童鞋就自己谷歌找找拉,临时用一用,几分钟的事情)
|
||||
* 2, cd到ip2region的根目录,然后运行如下命令:
|
||||
|
||||
```shell
|
||||
java -jar dbMaker-{version}.jar -src 文本数据文件 -region 地域csv文件 [-dst 生成的ip2region.db文件的目录]
|
||||
|
||||
# 文本数据文件:db文件的原始文本数据文件路径,自带的ip2region.db文件就是/data/ip.merge.txt生成而来的,你可以换成自己的或者更改/data/ip.merge.txt重新生成
|
||||
# 地域csv文件:该文件目的是方便配置ip2region进行数据关系的存储,得到的数据包含一个city_id,这个直接使用/data/origin/global_region.csv文件即可
|
||||
# ip2region.db文件的目录:是可选参数,没有指定的话会在当前目录生成一份./data/ip2region.db文件
|
||||
```
|
||||
|
||||
* 3, 获取生成的ip2region.db文件覆盖原来的ip2region.db文件即可
|
||||
* 4, 默认的ip2region.db文件生成命令:
|
||||
|
||||
```shell
|
||||
cd ip2region项目根目录
|
||||
java -jar dbMaker-1.2.2.jar -src ./data/ip.merge.txt -region ./data/global_region.csv
|
||||
|
||||
# 会看到一大片的输出
|
||||
```
|
||||
|
||||
* 5, 数据库文件的结构和原理请阅读 @冬芽 的blog:[“ip2region数据库文件的结构和原理”](http://dongyado.com/tool/2016/08/18/structure-of-ip2region-database-file/)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,436 @@
|
|||
//*******************************
|
||||
// Create By Rocher Kong
|
||||
// Github https://github.com/RocherKong
|
||||
// Date 2018.02.09
|
||||
//*******************************
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace IP2Region
|
||||
{
|
||||
public class DbSearcher
|
||||
{
|
||||
public static int BTREE_ALGORITHM = 1;
|
||||
public static int BINARY_ALGORITHM = 2;
|
||||
public static int MEMORY_ALGORITYM = 3;
|
||||
|
||||
/**
|
||||
* db config
|
||||
*/
|
||||
private DbConfig dbConfig = null;
|
||||
|
||||
/**
|
||||
* db file access handler
|
||||
*/
|
||||
private FileStream raf = null;
|
||||
|
||||
/**
|
||||
* header blocks buffer
|
||||
*/
|
||||
private long[] HeaderSip = null;
|
||||
private int[] HeaderPtr = null;
|
||||
private int headerLength;
|
||||
|
||||
/**
|
||||
* super blocks info
|
||||
*/
|
||||
private long firstIndexPtr = 0;
|
||||
private long lastIndexPtr = 0;
|
||||
private int totalIndexBlocks = 0;
|
||||
|
||||
/**
|
||||
* for memory mode
|
||||
* the original db binary string
|
||||
*/
|
||||
private byte[] dbBinStr = null;
|
||||
|
||||
/**
|
||||
* construct class
|
||||
*
|
||||
* @param bdConfig
|
||||
* @param dbFile
|
||||
* @throws FileNotFoundException
|
||||
*/
|
||||
public DbSearcher(DbConfig dbConfig, String dbFile)
|
||||
{
|
||||
this.dbConfig = dbConfig;
|
||||
raf = new FileStream(dbFile, FileMode.Open, FileAccess.Read);
|
||||
}
|
||||
|
||||
/**
|
||||
* get the region with a int ip address with memory binary search algorithm
|
||||
*
|
||||
* @param ip
|
||||
* @throws IOException
|
||||
*/
|
||||
public DataBlock MemorySearch(long ip)
|
||||
{
|
||||
int blen = IndexBlock.GetIndexBlockLength();
|
||||
if (dbBinStr == null)
|
||||
{
|
||||
dbBinStr = new byte[(int)raf.Length];
|
||||
raf.Seek(0L, SeekOrigin.Begin);
|
||||
raf.Read(dbBinStr, 0, dbBinStr.Length);
|
||||
|
||||
//initialize the global vars
|
||||
firstIndexPtr = Util.getIntLong(dbBinStr, 0);
|
||||
lastIndexPtr = Util.getIntLong(dbBinStr, 4);
|
||||
totalIndexBlocks = (int)((lastIndexPtr - firstIndexPtr) / blen) + 1;
|
||||
}
|
||||
|
||||
//search the index blocks to define the data
|
||||
int l = 0, h = totalIndexBlocks;
|
||||
long sip, eip, dataptr = 0;
|
||||
while (l <= h)
|
||||
{
|
||||
int m = (l + h) >> 1;
|
||||
int p = (int)(firstIndexPtr + m * blen);
|
||||
|
||||
sip = Util.getIntLong(dbBinStr, p);
|
||||
if (ip < sip)
|
||||
{
|
||||
h = m - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
eip = Util.getIntLong(dbBinStr, p + 4);
|
||||
if (ip > eip)
|
||||
{
|
||||
l = m + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
dataptr = Util.getIntLong(dbBinStr, p + 8);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//not matched
|
||||
if (dataptr == 0) return null;
|
||||
|
||||
//get the data
|
||||
int dataLen = (int)((dataptr >> 24) & 0xFF);
|
||||
int dataPtr = (int)((dataptr & 0x00FFFFFF));
|
||||
int city_id = (int)Util.getIntLong(dbBinStr, dataPtr);
|
||||
String region = System.Text.Encoding.UTF8.GetString(dbBinStr, dataPtr + 4, dataLen - 4);//new String(dbBinStr, dataPtr + 4, dataLen - 4, Encoding.UTF8);
|
||||
|
||||
return new DataBlock(city_id, region, dataPtr);
|
||||
}
|
||||
|
||||
/**
|
||||
* get the region throught the ip address with memory binary search algorithm
|
||||
*
|
||||
* @param ip
|
||||
* @return DataBlock
|
||||
* @throws IOException
|
||||
*/
|
||||
public DataBlock MemorySearch(String ip)
|
||||
{
|
||||
return MemorySearch(Util.ip2long(ip));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* get by index ptr
|
||||
*
|
||||
* @param indexPtr
|
||||
* @throws IOException
|
||||
*/
|
||||
public DataBlock GetByIndexPtr(long ptr)
|
||||
{
|
||||
raf.Seek(ptr, SeekOrigin.Begin);
|
||||
byte[]
|
||||
buffer = new byte[12];
|
||||
raf.Read(buffer, 0, buffer.Length);
|
||||
//long startIp = Util.getIntLong(buffer, 0);
|
||||
//long endIp = Util.getIntLong(buffer, 4);
|
||||
long extra = Util.getIntLong(buffer, 8);
|
||||
|
||||
int dataLen = (int)((extra >> 24) & 0xFF);
|
||||
int dataPtr = (int)((extra & 0x00FFFFFF));
|
||||
|
||||
raf.Seek(dataPtr, SeekOrigin.Begin);
|
||||
byte[] data = new byte[dataLen];
|
||||
raf.Read(data, 0, data.Length);
|
||||
|
||||
int city_id = (int)Util.getIntLong(data, 0);
|
||||
String region = Encoding.UTF8.GetString(data, 4, data.Length - 4);
|
||||
//new String(data, 4, data.Length - 4, "UTF-8");
|
||||
|
||||
return new DataBlock(city_id, region, dataPtr);
|
||||
}
|
||||
|
||||
/**
|
||||
* get the region with a int ip address with b-tree algorithm
|
||||
*
|
||||
* @param ip
|
||||
* @throws IOException
|
||||
*/
|
||||
public DataBlock BtreeSearch(long ip)
|
||||
{
|
||||
//check and load the header
|
||||
if (HeaderSip == null)
|
||||
{
|
||||
raf.Seek(8L, SeekOrigin.Begin); //pass the super block
|
||||
//byte[] b = new byte[dbConfig.getTotalHeaderSize()];
|
||||
byte[] b = new byte[4096];
|
||||
raf.Read(b, 0, b.Length);
|
||||
|
||||
//fill the header
|
||||
int len = b.Length >> 3, idx = 0; //b.lenght / 8
|
||||
HeaderSip = new long[len];
|
||||
HeaderPtr = new int[len];
|
||||
long startIp, dataPtrTemp;
|
||||
for (int i = 0; i < b.Length; i += 8)
|
||||
{
|
||||
startIp = Util.getIntLong(b, i);
|
||||
dataPtrTemp = Util.getIntLong(b, i + 4);
|
||||
if (dataPtrTemp == 0) break;
|
||||
|
||||
HeaderSip[idx] = startIp;
|
||||
HeaderPtr[idx] = (int)dataPtrTemp;
|
||||
idx++;
|
||||
}
|
||||
|
||||
headerLength = idx;
|
||||
}
|
||||
|
||||
//1. define the index block with the binary search
|
||||
if (ip == HeaderSip[0])
|
||||
{
|
||||
return GetByIndexPtr(HeaderPtr[0]);
|
||||
}
|
||||
else if (ip == HeaderSip[headerLength - 1])
|
||||
{
|
||||
return GetByIndexPtr(HeaderPtr[headerLength - 1]);
|
||||
}
|
||||
|
||||
int l = 0, h = headerLength, sptr = 0, eptr = 0;
|
||||
while (l <= h)
|
||||
{
|
||||
int m = (l + h) >> 1;
|
||||
|
||||
//perfetc matched, just return it
|
||||
if (ip == HeaderSip[m])
|
||||
{
|
||||
if (m > 0)
|
||||
{
|
||||
sptr = HeaderPtr[m - 1];
|
||||
eptr = HeaderPtr[m];
|
||||
}
|
||||
else
|
||||
{
|
||||
sptr = HeaderPtr[m];
|
||||
eptr = HeaderPtr[m + 1];
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
//less then the middle value
|
||||
if (ip < HeaderSip[m])
|
||||
{
|
||||
if (m == 0)
|
||||
{
|
||||
sptr = HeaderPtr[m];
|
||||
eptr = HeaderPtr[m + 1];
|
||||
break;
|
||||
}
|
||||
else if (ip > HeaderSip[m - 1])
|
||||
{
|
||||
sptr = HeaderPtr[m - 1];
|
||||
eptr = HeaderPtr[m];
|
||||
break;
|
||||
}
|
||||
h = m - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m == headerLength - 1)
|
||||
{
|
||||
sptr = HeaderPtr[m - 1];
|
||||
eptr = HeaderPtr[m];
|
||||
break;
|
||||
}
|
||||
else if (ip <= HeaderSip[m + 1])
|
||||
{
|
||||
sptr = HeaderPtr[m];
|
||||
eptr = HeaderPtr[m + 1];
|
||||
break;
|
||||
}
|
||||
l = m + 1;
|
||||
}
|
||||
}
|
||||
|
||||
//match nothing just stop it
|
||||
if (sptr == 0) return null;
|
||||
|
||||
//2. search the index blocks to define the data
|
||||
int blockLen = eptr - sptr, blen = IndexBlock.GetIndexBlockLength();
|
||||
byte[]
|
||||
iBuffer = new byte[blockLen + blen]; //include the right border block
|
||||
raf.Seek(sptr, SeekOrigin.Begin);
|
||||
raf.Read(iBuffer, 0, iBuffer.Length);
|
||||
|
||||
l = 0; h = blockLen / blen;
|
||||
long sip, eip, dataptr = 0;
|
||||
while (l <= h)
|
||||
{
|
||||
int m = (l + h) >> 1;
|
||||
int p = m * blen;
|
||||
sip = Util.getIntLong(iBuffer, p);
|
||||
if (ip < sip)
|
||||
{
|
||||
h = m - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
eip = Util.getIntLong(iBuffer, p + 4);
|
||||
if (ip > eip)
|
||||
{
|
||||
l = m + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
dataptr = Util.getIntLong(iBuffer, p + 8);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//not matched
|
||||
if (dataptr == 0) return null;
|
||||
|
||||
//3. get the data
|
||||
int dataLen = (int)((dataptr >> 24) & 0xFF);
|
||||
int dataPtr = (int)((dataptr & 0x00FFFFFF));
|
||||
|
||||
raf.Seek(dataPtr, SeekOrigin.Begin);
|
||||
byte[] data = new byte[dataLen];
|
||||
raf.Read(data, 0, data.Length);
|
||||
|
||||
int city_id = (int)Util.getIntLong(data, 0);
|
||||
String region = Encoding.UTF8.GetString(data, 4, data.Length - 4);// new String(data, 4, data.Length - 4, "UTF-8");
|
||||
|
||||
return new DataBlock(city_id, region, dataPtr);
|
||||
}
|
||||
|
||||
/**
|
||||
* get the region throught the ip address with b-tree search algorithm
|
||||
*
|
||||
* @param ip
|
||||
* @return DataBlock
|
||||
* @throws IOException
|
||||
*/
|
||||
public DataBlock BtreeSearch(String ip)
|
||||
{
|
||||
return BtreeSearch(Util.ip2long(ip));
|
||||
}
|
||||
|
||||
/**
|
||||
* get the region with a int ip address with binary search algorithm
|
||||
*
|
||||
* @param ip
|
||||
* @throws IOException
|
||||
*/
|
||||
public DataBlock BinarySearch(long ip)
|
||||
{
|
||||
int blen = IndexBlock.GetIndexBlockLength();
|
||||
if (totalIndexBlocks == 0)
|
||||
{
|
||||
raf.Seek(0L, SeekOrigin.Begin);
|
||||
byte[] superBytes = new byte[8];
|
||||
raf.Read(superBytes, 0, superBytes.Length);
|
||||
//initialize the global vars
|
||||
firstIndexPtr = Util.getIntLong(superBytes, 0);
|
||||
lastIndexPtr = Util.getIntLong(superBytes, 4);
|
||||
totalIndexBlocks = (int)((lastIndexPtr - firstIndexPtr) / blen) + 1;
|
||||
}
|
||||
|
||||
//search the index blocks to define the data
|
||||
int l = 0, h = totalIndexBlocks;
|
||||
byte[]
|
||||
buffer = new byte[blen];
|
||||
long sip, eip, dataptr = 0;
|
||||
while (l <= h)
|
||||
{
|
||||
int m = (l + h) >> 1;
|
||||
raf.Seek(firstIndexPtr + m * blen, SeekOrigin.Begin); //set the file pointer
|
||||
raf.Read(buffer, 0, buffer.Length);
|
||||
sip = Util.getIntLong(buffer, 0);
|
||||
if (ip < sip)
|
||||
{
|
||||
h = m - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
eip = Util.getIntLong(buffer, 4);
|
||||
if (ip > eip)
|
||||
{
|
||||
l = m + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
dataptr = Util.getIntLong(buffer, 8);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//not matched
|
||||
if (dataptr == 0) return null;
|
||||
|
||||
//get the data
|
||||
int dataLen = (int)((dataptr >> 24) & 0xFF);
|
||||
int dataPtr = (int)((dataptr & 0x00FFFFFF));
|
||||
|
||||
raf.Seek(dataPtr, SeekOrigin.Begin);
|
||||
byte[] data = new byte[dataLen];
|
||||
raf.Read(data, 0, data.Length);
|
||||
|
||||
int city_id = (int)Util.getIntLong(data, 0);
|
||||
String region = Encoding.UTF8.GetString(data, 4, data.Length - 4);//new String(data, 4, data.Length - 4, "UTF-8");
|
||||
|
||||
return new DataBlock(city_id, region, dataPtr);
|
||||
}
|
||||
|
||||
/**
|
||||
* get the region throught the ip address with binary search algorithm
|
||||
*
|
||||
* @param ip
|
||||
* @return DataBlock
|
||||
* @throws IOException
|
||||
*/
|
||||
public DataBlock BinarySearch(String ip)
|
||||
{
|
||||
return BinarySearch(Util.ip2long(ip));
|
||||
}
|
||||
|
||||
/**
|
||||
* get the db config
|
||||
*
|
||||
* @return DbConfig
|
||||
*/
|
||||
public DbConfig GetDbConfig()
|
||||
{
|
||||
return dbConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* close the db
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
public void Close()
|
||||
{
|
||||
HeaderSip = null; //let gc do its work
|
||||
HeaderPtr = null;
|
||||
dbBinStr = null;
|
||||
raf.Close();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,92 @@
|
|||
//*******************************
|
||||
// Create By Rocher Kong
|
||||
// Github https://github.com/RocherKong
|
||||
// Date 2018.02.09
|
||||
//*******************************
|
||||
using System;
|
||||
using System.Text;
|
||||
|
||||
namespace IP2Region
|
||||
{
|
||||
public class DataBlock
|
||||
{
|
||||
/**
|
||||
* city id
|
||||
*/
|
||||
private int city_id;
|
||||
|
||||
/**
|
||||
* region address
|
||||
*/
|
||||
private String region;
|
||||
|
||||
/**
|
||||
* region ptr in the db file
|
||||
*/
|
||||
private int dataPtr;
|
||||
|
||||
/**
|
||||
* construct method
|
||||
*
|
||||
* @param city_id
|
||||
* @param region region string
|
||||
* @param ptr data ptr
|
||||
*/
|
||||
public DataBlock(int city_id, String region, int dataPtr)
|
||||
{
|
||||
this.city_id = city_id;
|
||||
this.region = region;
|
||||
this.dataPtr = dataPtr;
|
||||
}
|
||||
|
||||
public DataBlock(int city_id, String region)
|
||||
{
|
||||
this.city_id = city_id;
|
||||
this.region = region;
|
||||
this.dataPtr = 0;
|
||||
}
|
||||
|
||||
public int GetCityId()
|
||||
{
|
||||
return city_id;
|
||||
}
|
||||
|
||||
public DataBlock SetCityId(int city_id)
|
||||
{
|
||||
this.city_id = city_id;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String GetRegion()
|
||||
{
|
||||
return region;
|
||||
}
|
||||
|
||||
public DataBlock SetRegion(String region)
|
||||
{
|
||||
this.region = region;
|
||||
return this;
|
||||
}
|
||||
|
||||
public int GetDataPtr()
|
||||
{
|
||||
return dataPtr;
|
||||
}
|
||||
|
||||
public DataBlock SetDataPtr(int dataPtr)
|
||||
{
|
||||
this.dataPtr = dataPtr;
|
||||
return this;
|
||||
}
|
||||
|
||||
public override String ToString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
sb.Append(city_id).Append('|').Append(region).Append('|').Append(dataPtr);
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
//*******************************
|
||||
// Create By Rocher Kong
|
||||
// Github https://github.com/RocherKong
|
||||
// Date 2018.02.09
|
||||
//*******************************
|
||||
using System;
|
||||
|
||||
namespace IP2Region
|
||||
{
|
||||
public class DbConfig
|
||||
{
|
||||
/**
|
||||
* total header data block size
|
||||
*/
|
||||
private int totalHeaderSize;
|
||||
|
||||
/**
|
||||
* max index data block size
|
||||
* u should always choice the fastest read block size
|
||||
*/
|
||||
private int indexBlockSize;
|
||||
|
||||
/**
|
||||
* construct method
|
||||
*
|
||||
* @param totalHeaderSize
|
||||
* @param dataBlockSize
|
||||
* @throws DbMakerConfigException
|
||||
*/
|
||||
public DbConfig(int totalHeaderSize)
|
||||
{
|
||||
if ((totalHeaderSize % 8) != 0)
|
||||
{
|
||||
throw new DbMakerConfigException("totalHeaderSize must be times of 8");
|
||||
}
|
||||
this.totalHeaderSize = totalHeaderSize;
|
||||
this.indexBlockSize = 8192; //4 * 2048
|
||||
}
|
||||
|
||||
public DbConfig()
|
||||
{
|
||||
this.totalHeaderSize = 8 * 2048;
|
||||
this.indexBlockSize = 8192;
|
||||
}
|
||||
|
||||
public int GetTotalHeaderSize()
|
||||
{
|
||||
return totalHeaderSize;
|
||||
}
|
||||
|
||||
public DbConfig SetTotalHeaderSize(int totalHeaderSize)
|
||||
{
|
||||
this.totalHeaderSize = totalHeaderSize;
|
||||
return this;
|
||||
}
|
||||
|
||||
public int GetIndexBlockSize()
|
||||
{
|
||||
return indexBlockSize;
|
||||
}
|
||||
|
||||
public DbConfig SetIndexBlockSize(int dataBlockSize)
|
||||
{
|
||||
this.indexBlockSize = dataBlockSize;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
//*******************************
|
||||
// Create By Rocher Kong
|
||||
// Github https://github.com/RocherKong
|
||||
// Date 2018.02.09
|
||||
//*******************************
|
||||
namespace IP2Region
|
||||
{
|
||||
public class DbMakerConfigException : System.Exception
|
||||
{
|
||||
public string ErrorCode { get; set; }
|
||||
public DbMakerConfigException(string ErrorCode)
|
||||
{
|
||||
this.ErrorCode=ErrorCode;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
//*******************************
|
||||
// Create By Rocher Kong
|
||||
// Github https://github.com/RocherKong
|
||||
// Date 2018.02.09
|
||||
//*******************************
|
||||
|
||||
using System;
|
||||
|
||||
namespace IP2Region
|
||||
{
|
||||
|
||||
public class HeaderBlock
|
||||
{
|
||||
/**
|
||||
* index block start ip address
|
||||
*/
|
||||
private long indexStartIp;
|
||||
|
||||
/**
|
||||
* ip address
|
||||
*/
|
||||
private int indexPtr;
|
||||
|
||||
public HeaderBlock(long indexStartIp, int indexPtr)
|
||||
{
|
||||
this.indexStartIp = indexStartIp;
|
||||
this.indexPtr = indexPtr;
|
||||
}
|
||||
|
||||
public long GetIndexStartIp()
|
||||
{
|
||||
return indexStartIp;
|
||||
}
|
||||
|
||||
public HeaderBlock SetIndexStartIp(long indexStartIp)
|
||||
{
|
||||
this.indexStartIp = indexStartIp;
|
||||
return this;
|
||||
}
|
||||
|
||||
public int GetIndexPtr()
|
||||
{
|
||||
return indexPtr;
|
||||
}
|
||||
|
||||
public HeaderBlock SetIndexPtr(int indexPtr)
|
||||
{
|
||||
this.indexPtr = indexPtr;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* get the bytes for db storage
|
||||
*
|
||||
* @return byte[]
|
||||
*/
|
||||
public byte[] GetBytes()
|
||||
{
|
||||
/*
|
||||
* +------------+-----------+
|
||||
* | 4bytes | 4bytes |
|
||||
* +------------+-----------+
|
||||
* start ip index ptr
|
||||
*/
|
||||
byte[] b = new byte[8];
|
||||
|
||||
Util.writeIntLong(b, 0, indexStartIp);
|
||||
Util.writeIntLong(b, 4, indexPtr);
|
||||
|
||||
return b;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>netstandard2.0;net46</TargetFrameworks>
|
||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||
<Authors>Rocher</Authors>
|
||||
<Company>Rocher</Company>
|
||||
<Description>准确率99.9%的ip地址定位库,0.0x毫秒级查询, 支持Binary,B树,内存三种查询算法,妈妈再也不用担心我的ip地址定位!</Description>
|
||||
<PackageReleaseNotes>ip2region - 最自由的ip地址查询库,ip到地区的映射库,提供Binary,B树和纯内存三种查询算法,妈妈再也不用担心我的ip地址定位。</PackageReleaseNotes>
|
||||
<Copyright>Rocher</Copyright>
|
||||
<PackageTags>ip2region c# IP</PackageTags>
|
||||
<RepositoryUrl>https://gitee.com/rocherkong/IP2Region</RepositoryUrl>
|
||||
<Version>1.1.0</Version>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="DBFile\ip2region.db">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
@ -0,0 +1,117 @@
|
|||
//*******************************
|
||||
// Create By Rocher Kong
|
||||
// Github https://github.com/RocherKong
|
||||
// Date 2018.02.09
|
||||
//*******************************
|
||||
using System;
|
||||
|
||||
namespace IP2Region
|
||||
{
|
||||
|
||||
public class IndexBlock
|
||||
{
|
||||
private static int LENGTH = 12;
|
||||
|
||||
/**
|
||||
* start ip address
|
||||
*/
|
||||
private long startIp;
|
||||
|
||||
/**
|
||||
* end ip address
|
||||
*/
|
||||
private long endIp;
|
||||
|
||||
/**
|
||||
* data ptr and data length
|
||||
*/
|
||||
private uint dataPtr;
|
||||
|
||||
/**
|
||||
* data length
|
||||
*/
|
||||
private int dataLen;
|
||||
|
||||
public IndexBlock(long startIp, long endIp, uint dataPtr, int dataLen)
|
||||
{
|
||||
this.startIp = startIp;
|
||||
this.endIp = endIp;
|
||||
this.dataPtr = dataPtr;
|
||||
this.dataLen = dataLen;
|
||||
}
|
||||
|
||||
public long GetStartIp()
|
||||
{
|
||||
return startIp;
|
||||
}
|
||||
|
||||
public IndexBlock SetStartIp(long startIp)
|
||||
{
|
||||
this.startIp = startIp;
|
||||
return this;
|
||||
}
|
||||
|
||||
public long GetEndIp()
|
||||
{
|
||||
return endIp;
|
||||
}
|
||||
|
||||
public IndexBlock SetEndIp(long endIp)
|
||||
{
|
||||
this.endIp = endIp;
|
||||
return this;
|
||||
}
|
||||
|
||||
public uint GetDataPtr()
|
||||
{
|
||||
return dataPtr;
|
||||
}
|
||||
|
||||
public IndexBlock SetDataPtr(uint dataPtr)
|
||||
{
|
||||
this.dataPtr = dataPtr;
|
||||
return this;
|
||||
}
|
||||
|
||||
public int GetDataLen()
|
||||
{
|
||||
return dataLen;
|
||||
}
|
||||
|
||||
public IndexBlock SetDataLen(int dataLen)
|
||||
{
|
||||
this.dataLen = dataLen;
|
||||
return this;
|
||||
}
|
||||
|
||||
public static int GetIndexBlockLength()
|
||||
{
|
||||
return LENGTH;
|
||||
}
|
||||
|
||||
/**
|
||||
* get the bytes for storage
|
||||
*
|
||||
* @return byte[]
|
||||
*/
|
||||
public byte[] GetBytes()
|
||||
{
|
||||
/*
|
||||
* +------------+-----------+-----------+
|
||||
* | 4bytes | 4bytes | 4bytes |
|
||||
* +------------+-----------+-----------+
|
||||
* start ip end ip data ptr + len
|
||||
*/
|
||||
byte[] b = new byte[12];
|
||||
|
||||
Util.writeIntLong(b, 0, startIp); //start ip
|
||||
Util.writeIntLong(b, 4, endIp); //end ip
|
||||
|
||||
//write the data ptr and the length
|
||||
long mix = dataPtr | ((dataLen << 24) & 0xFF000000L);
|
||||
Util.writeIntLong(b, 8, mix);
|
||||
|
||||
return b;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,162 @@
|
|||
//*******************************
|
||||
// Create By Rocher Kong
|
||||
// Github https://github.com/RocherKong
|
||||
// Date 2018.02.09
|
||||
//*******************************
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Text;
|
||||
|
||||
namespace IP2Region
|
||||
{
|
||||
|
||||
public class Util
|
||||
{
|
||||
/**
|
||||
* write specfield bytes to a byte array start from offset
|
||||
*
|
||||
* @param b
|
||||
* @param offset
|
||||
* @param v
|
||||
* @param bytes
|
||||
*/
|
||||
public static void write(byte[] b, int offset, ulong v, int bytes)
|
||||
{
|
||||
for (int i = 0; i < bytes; i++)
|
||||
{
|
||||
b[offset++] = (byte)((v >> (8 * i)) & 0xFF);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* write a int to a byte array
|
||||
*
|
||||
* @param b
|
||||
* @param offet
|
||||
* @param v
|
||||
*/
|
||||
public static void writeIntLong(byte[] b, int offset, long v)
|
||||
{
|
||||
b[offset++] = (byte)((v >> 0) & 0xFF);
|
||||
b[offset++] = (byte)((v >> 8) & 0xFF);
|
||||
b[offset++] = (byte)((v >> 16) & 0xFF);
|
||||
b[offset] = (byte)((v >> 24) & 0xFF);
|
||||
}
|
||||
|
||||
/**
|
||||
* get a int from a byte array start from the specifiled offset
|
||||
*
|
||||
* @param b
|
||||
* @param offset
|
||||
*/
|
||||
public static long getIntLong(byte[] b, int offset)
|
||||
{
|
||||
return (
|
||||
((b[offset++] & 0x000000FFL)) |
|
||||
((b[offset++] << 8) & 0x0000FF00L) |
|
||||
((b[offset++] << 16) & 0x00FF0000L) |
|
||||
((b[offset] << 24) & 0xFF000000L)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* get a int from a byte array start from the specifield offset
|
||||
*
|
||||
* @param b
|
||||
* @param offset
|
||||
*/
|
||||
public static int getInt3(byte[] b, int offset)
|
||||
{
|
||||
return (
|
||||
(b[offset++] & 0x000000FF) |
|
||||
(b[offset++] & 0x0000FF00) |
|
||||
(b[offset] & 0x00FF0000)
|
||||
);
|
||||
}
|
||||
|
||||
public static int getInt2(byte[] b, int offset)
|
||||
{
|
||||
return (
|
||||
(b[offset++] & 0x000000FF) |
|
||||
(b[offset] & 0x0000FF00)
|
||||
);
|
||||
}
|
||||
|
||||
public static int getInt1(byte[] b, int offset)
|
||||
{
|
||||
return (
|
||||
(b[offset] & 0x000000FF)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* string ip to long ip
|
||||
*
|
||||
* @param ip
|
||||
* @return long
|
||||
*/
|
||||
public static long ip2long(string ip)
|
||||
{
|
||||
string[] p = ip.Split('.');
|
||||
if (p.Length != 4) return 0;
|
||||
var bip1 = long.TryParse(p[0], out long ip1);
|
||||
var bip2 = long.TryParse(p[1], out long ip2);
|
||||
var bip3 = long.TryParse(p[2], out long ip3);
|
||||
var bip4 = long.TryParse(p[3], out long ip4);
|
||||
|
||||
if (!bip1 || !bip2 || !bip3 || !bip4
|
||||
|| ip4 > 255 || ip1 > 255 || ip2 > 255 || ip3 > 255
|
||||
|| ip4 < 1 || ip1 < 1 || ip2 < 1 || ip3 < 1)
|
||||
{
|
||||
throw new Exception("IP Illegal.");
|
||||
}
|
||||
|
||||
long p1 = ((ip1 << 24) & 0xFF000000);
|
||||
long p2 = ((ip2 << 16) & 0x00FF0000);
|
||||
long p3 = ((ip3 << 8) & 0x0000FF00);
|
||||
long p4 = ((ip4 << 0) & 0x000000FF);
|
||||
return ((p1 | p2 | p3 | p4) & 0xFFFFFFFFL);
|
||||
}
|
||||
|
||||
/**
|
||||
* int to ip string
|
||||
*
|
||||
* @param ip
|
||||
* @return string
|
||||
*/
|
||||
public static string long2ip(long ip)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
sb
|
||||
.Append((ip >> 24) & 0xFF).Append('.')
|
||||
.Append((ip >> 16) & 0xFF).Append('.')
|
||||
.Append((ip >> 8) & 0xFF).Append('.')
|
||||
.Append((ip >> 0) & 0xFF);
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
/**
|
||||
* check the validate of the specifeld ip address
|
||||
*
|
||||
* @param ip
|
||||
* @return boolean
|
||||
*/
|
||||
public static Boolean isIpAddress(string ip)
|
||||
{
|
||||
string[] p = ip.Split('.');
|
||||
if (p.Length != 4) return false;
|
||||
|
||||
foreach (string pp in p)
|
||||
{
|
||||
if (pp.Length > 3) return false;
|
||||
int val = int.Parse(pp);
|
||||
if (val > 255) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Binary file not shown.
|
|
@ -0,0 +1,24 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\IP2Region\IP2Region.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Reference Include="netstandard">
|
||||
<HintPath>C:\Program Files (x86)\Microsoft SDKs\NuGetPackagesFallback\netstandard.library\2.0.1\build\netstandard2.0\ref\netstandard.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="DBFile\ip2region.db">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
using IP2Region;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace IP2Region_ConsoleTest
|
||||
{
|
||||
class Program
|
||||
{
|
||||
static void Main(string[] args)
|
||||
{
|
||||
DbSearcher dbSearcher = new DbSearcher(new DbConfig
|
||||
{
|
||||
|
||||
}, AppContext.BaseDirectory + @"/DBFile/ip2region.db");
|
||||
string ipAddress = "";
|
||||
DataBlock result = null, result2 = null,result3=null;
|
||||
Console.WriteLine("请输入IP地址:");
|
||||
ipAddress = Console.ReadLine();
|
||||
Stopwatch sw = new Stopwatch();
|
||||
do
|
||||
{
|
||||
sw.Start();
|
||||
try
|
||||
{
|
||||
result = dbSearcher.BtreeSearch(ipAddress);
|
||||
sw.Stop();
|
||||
Console.WriteLine("[btree]你的IP所属区域为:" + result.GetRegion() + ";耗时:" + sw.Elapsed.TotalMilliseconds + "ms");
|
||||
sw.Start();
|
||||
result2 = dbSearcher.BinarySearch(ipAddress);
|
||||
sw.Stop();
|
||||
Console.WriteLine("[binarySearch]你的IP所属区域为:" + result2.GetRegion() + ";耗时:" + sw.Elapsed.TotalMilliseconds + "ms");
|
||||
sw.Start();
|
||||
result3 = dbSearcher.MemorySearch(ipAddress);
|
||||
sw.Stop();
|
||||
Console.WriteLine("[MemorySearch]你的IP所属区域为:" + result3.GetRegion() + ";耗时:" + sw.Elapsed.TotalMilliseconds + "ms");
|
||||
Console.WriteLine("请输入IP地址:");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
Console.WriteLine(ex.Message);
|
||||
sw.Stop();
|
||||
}
|
||||
|
||||
//Console.WriteLine("结束请输入bye");
|
||||
|
||||
} while ((ipAddress = Console.ReadLine()) != "bye");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
This file is used by the publish/package process of your project. You can customize the behavior of this process
|
||||
by editing this MSBuild file. In order to learn more about this please visit https://go.microsoft.com/fwlink/?LinkID=208121.
|
||||
-->
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<PublishProtocol>FileSystem</PublishProtocol>
|
||||
<Configuration>Release</Configuration>
|
||||
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||
<PublishDir>bin\Release\PublishOutput</PublishDir>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
This file is used by the publish/package process of your project. You can customize the behavior of this process
|
||||
by editing this MSBuild file. In order to learn more about this please visit https://go.microsoft.com/fwlink/?LinkID=208121.
|
||||
-->
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
</Project>
|
||||
|
|
@ -43,7 +43,9 @@ typedef unsigned int uint_t;
|
|||
typedef unsigned long ulong_t;
|
||||
|
||||
#define INDEX_BLOCK_LENGTH 12
|
||||
#define TOTAL_HEADER_LENGTH 4096
|
||||
|
||||
//thats 8 * 1024
|
||||
#define TOTAL_HEADER_LENGTH 8192
|
||||
|
||||
/*
|
||||
* ip2region properties struct
|
||||
|
|
|
|||
|
|
@ -0,0 +1,328 @@
|
|||
/**
|
||||
* ip2region implementation with mmap support created by Leo Ma
|
||||
*
|
||||
* @see #ip2region.h
|
||||
* @author chenxin<chenxin619315@gmail.com>
|
||||
* @author Leo Ma<http://git.oschina.net/begeekmyfriend>
|
||||
* @date 2017/01/12
|
||||
*/
|
||||
|
||||
#include "ip2region.h"
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
/**
|
||||
* create a new ip2region object
|
||||
*
|
||||
* @param dbFile path
|
||||
*/
|
||||
IP2R_API uint_t ip2region_create(ip2region_t ip2rObj, char *dbFile)
|
||||
{
|
||||
struct stat st;
|
||||
|
||||
memset(ip2rObj, 0x00, sizeof(ip2region_entry));
|
||||
ip2rObj->dbFile = IP2R_MALLOC(1024);
|
||||
strcpy(ip2rObj->dbFile, dbFile);
|
||||
|
||||
int fd = open(dbFile, O_RDONLY);
|
||||
if ( fd < 0 ) {
|
||||
//fprintf(stderr, "Fail to open the db file %s\n", ip2rObj->dbFile);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ( fstat(fd, &st) < 0 ) {
|
||||
//fprintf(stderr, "Fail to stat the db file %s\n", ip2rObj->dbFile);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ip2rObj->dbSize = st.st_size;
|
||||
ip2rObj->dbBinStr = mmap(NULL, ip2rObj->dbSize, PROT_READ, MAP_PRIVATE, fd, 0);
|
||||
if ( ip2rObj->dbBinStr == (void *) -1 ) {
|
||||
//fprintf(stderr, "Fail to map the db file %s\n", ip2rObj->dbFile);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Close db file as soon as possible.
|
||||
close(fd);
|
||||
|
||||
ip2rObj->headerLen = 0;
|
||||
ip2rObj->HeaderSip = (uint_t *) IP2R_MALLOC(TOTAL_HEADER_LENGTH);
|
||||
if ( ip2rObj->HeaderSip == NULL ) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
ip2rObj->HeaderPtr = (uint_t *) IP2R_MALLOC(TOTAL_HEADER_LENGTH);
|
||||
if ( ip2rObj->HeaderPtr == NULL ) {
|
||||
IP2R_FREE(ip2rObj->HeaderSip);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ip2rObj->firstIndexPtr = getUnsignedInt(ip2rObj->dbBinStr, 0);
|
||||
ip2rObj->lastIndexPtr = getUnsignedInt(ip2rObj->dbBinStr, 4);
|
||||
ip2rObj->totalBlocks = (ip2rObj->lastIndexPtr-ip2rObj->firstIndexPtr)/INDEX_BLOCK_LENGTH + 1;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* destroy the specifield ip2region object
|
||||
*
|
||||
* @param ip2region_t
|
||||
*/
|
||||
IP2R_API uint_t ip2region_destroy(ip2region_t ip2rObj)
|
||||
{
|
||||
IP2R_FREE(ip2rObj->dbFile);
|
||||
IP2R_FREE(ip2rObj->HeaderSip);
|
||||
IP2R_FREE(ip2rObj->HeaderPtr);
|
||||
|
||||
//free the db binary string
|
||||
if ( ip2rObj->dbBinStr != NULL ) {
|
||||
munmap(ip2rObj->dbBinStr, ip2rObj->dbSize);
|
||||
}
|
||||
|
||||
memset(ip2rObj, 0x00, sizeof(ip2region_entry));
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* get the region associated with the specified ip address with the memory binary search algorithm
|
||||
*
|
||||
* @param ip2rObj
|
||||
* @param ip
|
||||
* @param datablock
|
||||
*/
|
||||
IP2R_API uint_t ip2region_binary_search(ip2region_t ip2rObj, uint_t ip, datablock_t datablock)
|
||||
{
|
||||
int l, h, m, p;
|
||||
uint_t sip, eip, dptr;
|
||||
int dataLen, dataptr;
|
||||
char *buffer;
|
||||
|
||||
l = 0; h = ip2rObj->totalBlocks; dptr = 0;
|
||||
while ( l <= h ) {
|
||||
m = (l + h) >> 1;
|
||||
p = ip2rObj->firstIndexPtr + m * INDEX_BLOCK_LENGTH;
|
||||
|
||||
buffer = ip2rObj->dbBinStr + p;
|
||||
sip = getUnsignedInt(buffer, 0);
|
||||
if ( ip < sip ) {
|
||||
h = m - 1;
|
||||
} else {
|
||||
eip = getUnsignedInt(buffer, 4);
|
||||
if ( ip > eip ) {
|
||||
l = m + 1;
|
||||
} else {
|
||||
dptr = getUnsignedInt(buffer, 8);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( dptr == 0 ) return 0;
|
||||
|
||||
//get the data
|
||||
dataLen = ((dptr >> 24) & 0xFF);
|
||||
dataptr = (dptr & 0x00FFFFFF);
|
||||
buffer = ip2rObj->dbBinStr + dataptr;
|
||||
|
||||
//fill the data to the datablock
|
||||
datablock->city_id = getUnsignedInt(buffer, 0);
|
||||
dataLen -= 4; //reduce the length of the city_id
|
||||
memcpy(datablock->region, buffer + 4, dataLen);
|
||||
datablock->region[dataLen] = '\0';
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
IP2R_API uint_t ip2region_binary_search_string(ip2region_t ip2rObj, char *ip, datablock_t datablock)
|
||||
{
|
||||
return ip2region_binary_search(ip2rObj, ip2long(ip), datablock);
|
||||
}
|
||||
|
||||
/**
|
||||
* get the region associated with the specifield ip address with b-tree algorithm
|
||||
*
|
||||
* @param ip2rObj
|
||||
* @param ip
|
||||
* @param datablock
|
||||
* @return uint_t
|
||||
*/
|
||||
IP2R_API uint_t ip2region_btree_search(ip2region_t ip2rObj, uint_t ip, datablock_t datablock)
|
||||
{
|
||||
int i, j;
|
||||
int l, m, h, p, sptr, eptr, indexBlockLen, dataLen, dataptr;
|
||||
uint_t sip, eip, idxptr, dptr;
|
||||
char *buffer;
|
||||
|
||||
if ( ip2rObj->headerLen == 0 ) {
|
||||
buffer = ip2rObj->dbBinStr + 8;
|
||||
for ( i = j = 0; i < TOTAL_HEADER_LENGTH; i += 8, j++ ) {
|
||||
sip = getUnsignedInt(buffer, i);
|
||||
idxptr = getUnsignedInt(buffer, i + 4);
|
||||
if ( idxptr == 0 ) break;
|
||||
|
||||
ip2rObj->HeaderSip[j] = sip;
|
||||
ip2rObj->HeaderPtr[j] = idxptr;
|
||||
}
|
||||
|
||||
ip2rObj->headerLen = j;
|
||||
}
|
||||
|
||||
//search the header block to define the index block
|
||||
l = 0; h = ip2rObj->headerLen; sptr = 0; eptr = 0;
|
||||
while ( l <= h ) {
|
||||
m = ((l + h) >> 1);
|
||||
|
||||
//perfetc matched, just return it
|
||||
if ( ip == ip2rObj->HeaderSip[m] ) {
|
||||
if ( m > 0 ) {
|
||||
sptr = ip2rObj->HeaderPtr[m-1];
|
||||
eptr = ip2rObj->HeaderPtr[m ];
|
||||
} else {
|
||||
sptr = ip2rObj->HeaderPtr[m ];
|
||||
eptr = ip2rObj->HeaderPtr[m+1];
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
//less then the middle value
|
||||
if ( ip < ip2rObj->HeaderSip[m] ) {
|
||||
if ( m == 0 ) {
|
||||
sptr = ip2rObj->HeaderPtr[m ];
|
||||
eptr = ip2rObj->HeaderPtr[m+1];
|
||||
break;
|
||||
} else if ( ip > ip2rObj->HeaderSip[m-1] ) {
|
||||
sptr = ip2rObj->HeaderPtr[m-1];
|
||||
eptr = ip2rObj->HeaderPtr[m ];
|
||||
break;
|
||||
}
|
||||
h = m - 1;
|
||||
} else {
|
||||
if ( m == ip2rObj->headerLen - 1 ) {
|
||||
sptr = ip2rObj->HeaderPtr[m-1];
|
||||
eptr = ip2rObj->HeaderPtr[m ];
|
||||
break;
|
||||
} else if ( ip <= ip2rObj->HeaderSip[m+1] ) {
|
||||
sptr = ip2rObj->HeaderPtr[m ];
|
||||
eptr = ip2rObj->HeaderPtr[m+1];
|
||||
break;
|
||||
}
|
||||
l = m + 1;
|
||||
}
|
||||
}
|
||||
|
||||
//not matched just stop it
|
||||
if ( sptr == 0 ) return 0;
|
||||
|
||||
indexBlockLen = eptr - sptr;
|
||||
buffer = ip2rObj->dbBinStr + sptr;
|
||||
|
||||
dptr = 0; l = 0; h = indexBlockLen / INDEX_BLOCK_LENGTH;
|
||||
while ( l <= h ) {
|
||||
m = ((l + h) >> 1);
|
||||
p = m * INDEX_BLOCK_LENGTH;
|
||||
sip = getUnsignedInt(buffer, p);
|
||||
if ( ip < sip ) {
|
||||
h = m - 1;
|
||||
} else {
|
||||
eip = getUnsignedInt(buffer, p + 4);
|
||||
if ( ip > eip ) {
|
||||
l = m + 1;
|
||||
} else {
|
||||
dptr = getUnsignedInt(buffer, p + 8);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( dptr == 0 ) return 0;
|
||||
|
||||
dataLen = ((dptr >> 24) & 0xFF);
|
||||
dataptr = (dptr & 0x00FFFFFF);
|
||||
buffer = ip2rObj->dbBinStr + dataptr;
|
||||
|
||||
datablock->city_id = getUnsignedInt(buffer, 0);
|
||||
dataLen -= 4;
|
||||
memcpy(datablock->region, buffer + 4, dataLen);
|
||||
datablock->region[dataLen] = '\0';
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
IP2R_API uint_t ip2region_btree_search_string(ip2region_t ip2rObj, char *ip, datablock_t datablock)
|
||||
{
|
||||
return ip2region_btree_search(ip2rObj, ip2long(ip), datablock);
|
||||
}
|
||||
|
||||
/**
|
||||
* get a unsinged long(4bytes) from a specifield buffer start from the specifield offset
|
||||
*
|
||||
* @param buffer
|
||||
* @param offset
|
||||
* @return uint_t
|
||||
*/
|
||||
IP2R_API uint_t getUnsignedInt(char *buffer, int offset)
|
||||
{
|
||||
return (
|
||||
((buffer[offset ]) & 0x000000FF) |
|
||||
((buffer[offset+1] << 8) & 0x0000FF00) |
|
||||
((buffer[offset+2] << 16) & 0x00FF0000) |
|
||||
((buffer[offset+3] << 24) & 0xFF000000)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* string ip to long
|
||||
*
|
||||
* @param ip
|
||||
* @return uint_t
|
||||
*/
|
||||
IP2R_API uint_t ip2long(char *ip)
|
||||
{
|
||||
int i = 0, p = 24;
|
||||
char buffer[4], *cs = ip;
|
||||
uint_t ipval = 0;
|
||||
|
||||
while ( *cs != '\0' ) {
|
||||
if ( *cs == '.' ) {
|
||||
//single part length limit
|
||||
if ( i > 3 ) {
|
||||
ipval = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if ( p < 0 ) break;
|
||||
buffer[i] = '\0';
|
||||
ipval |= (atoi(buffer) << p);
|
||||
p -= 8;
|
||||
i = 0;
|
||||
} else {
|
||||
buffer[i++] = *cs;
|
||||
}
|
||||
|
||||
cs++;
|
||||
}
|
||||
|
||||
//append the rest parts
|
||||
if ( i > 3 ) return 0;
|
||||
buffer[i] = '\0';
|
||||
ipval |= atoi(buffer);
|
||||
|
||||
return ipval;
|
||||
}
|
||||
|
||||
/**
|
||||
* long to string ip
|
||||
*
|
||||
* @param ip
|
||||
* @param buffer
|
||||
* @return uint_t(1 for success and 0 for failed)
|
||||
*/
|
||||
IP2R_API uint_t long2ip(uint_t ip, char *buffer)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,138 @@
|
|||
/**
|
||||
* ip2region header file
|
||||
*
|
||||
* @author chenxin<chenxin619315@gmail.com>
|
||||
* @author Leo Ma<http://git.oschina.net/begeekmyfriend>
|
||||
*/
|
||||
|
||||
#ifndef _IP2REGION_H
|
||||
#define _IP2REGION_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
//yat, just take it as this way, 99 percent and they works find
|
||||
#if ( defined(_WIN32) || defined(_WINDOWS_) || defined(__WINDOWS_) )
|
||||
# define OS_WINDOW
|
||||
#else
|
||||
# define OS_LINUX
|
||||
#endif
|
||||
|
||||
#ifdef OS_WINDOW
|
||||
# define IP2R_API extern __declspec(dllexport)
|
||||
#else
|
||||
/*platform shared library statement :: unix*/
|
||||
# define IP2R_API extern
|
||||
#endif
|
||||
|
||||
#define print(str) printf("%s", str)
|
||||
#define println(str) printf("%s\n", str)
|
||||
|
||||
/*
|
||||
* memory allocation macro definition.
|
||||
* cause we should use emalloc,ecalloc .ege. in php.
|
||||
* so you could make it better apdat the php environment.
|
||||
*/
|
||||
#define IP2R_CALLOC(_bytes, _blocks) calloc(_bytes, _blocks)
|
||||
#define IP2R_MALLOC(_bytes) malloc(_bytes)
|
||||
#define IP2R_FREE(_ptr) free(_ptr)
|
||||
|
||||
typedef unsigned short ushort_t;
|
||||
typedef unsigned char uchar_t;
|
||||
typedef unsigned int uint_t;
|
||||
typedef unsigned long ulong_t;
|
||||
|
||||
#define INDEX_BLOCK_LENGTH 12
|
||||
#define TOTAL_HEADER_LENGTH 8192
|
||||
|
||||
/*
|
||||
* ip2region properties struct
|
||||
*/
|
||||
typedef struct {
|
||||
uint_t *HeaderSip; //header start ip blocks
|
||||
uint_t *HeaderPtr; //header ptr blocks
|
||||
uint_t headerLen; //header block number
|
||||
char *dbFile; //path of db file
|
||||
char *dbBinStr; //db binary string for memory search mode
|
||||
uint_t dbSize;
|
||||
uint_t firstIndexPtr; //first index ptr
|
||||
uint_t lastIndexPtr; //last index ptr
|
||||
uint_t totalBlocks; //total index blocks number
|
||||
} ip2region_entry;
|
||||
typedef ip2region_entry * ip2region_t;
|
||||
|
||||
/*
|
||||
* data block
|
||||
*/
|
||||
typedef struct {
|
||||
uint_t city_id;
|
||||
char region[256];
|
||||
} datablock_entry;
|
||||
typedef datablock_entry * datablock_t;
|
||||
|
||||
|
||||
/**
|
||||
* create a new ip2region object
|
||||
*
|
||||
* @param ip2rObj
|
||||
* @param dbFile path
|
||||
*/
|
||||
IP2R_API uint_t ip2region_create(ip2region_t, char *);
|
||||
|
||||
/**
|
||||
* destroy the specified ip2region object
|
||||
*
|
||||
* @param ip2region_t
|
||||
*/
|
||||
IP2R_API uint_t ip2region_destroy(ip2region_t);
|
||||
|
||||
/**
|
||||
* get the region associated with the specified ip address with memory binary search algorithm
|
||||
*
|
||||
* @param ip2region_t
|
||||
* @param uint_t
|
||||
* @param datablock_t
|
||||
* @date 2016/06/30
|
||||
*/
|
||||
IP2R_API uint_t ip2region_binary_search(ip2region_t, uint_t, datablock_t);
|
||||
IP2R_API uint_t ip2region_binary_search_string(ip2region_t, char *, datablock_t);
|
||||
|
||||
/**
|
||||
* get the region associated with the specified ip address with b-tree algorithm
|
||||
*
|
||||
* @param ip2rObj
|
||||
* @param ip
|
||||
* @param datablock
|
||||
* @return uint_t
|
||||
*/
|
||||
IP2R_API uint_t ip2region_btree_search(ip2region_t, uint_t, datablock_t);
|
||||
IP2R_API uint_t ip2region_btree_search_string(ip2region_t, char *, datablock_t);
|
||||
|
||||
/**
|
||||
* get a unsinged long(4bytes) from a specified buffer start from the specified offset
|
||||
*
|
||||
* @param buffer
|
||||
* @param offset
|
||||
* @return uint_t
|
||||
*/
|
||||
IP2R_API uint_t getUnsignedInt(char *, int);
|
||||
|
||||
/**
|
||||
* string ip to long
|
||||
*
|
||||
* @param ip
|
||||
* @return uint_t
|
||||
*/
|
||||
IP2R_API uint_t ip2long(char *);
|
||||
|
||||
/**
|
||||
* long to string ip
|
||||
*
|
||||
* @param ip
|
||||
* @param buffer
|
||||
* @return uint_t(1 for success and 0 for failed)
|
||||
*/
|
||||
IP2R_API uint_t long2ip(uint_t, char *);
|
||||
|
||||
#endif /*end ifndef*/
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
/**
|
||||
* test ip2region searcher program
|
||||
*
|
||||
* @author chenxin<chenxin619315@gmail.com>
|
||||
* @date 2015-10-30
|
||||
*/
|
||||
|
||||
#include "ip2region.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#define __PRINT_ABOUT__ \
|
||||
println("+-------------------------------------+"); \
|
||||
println("| ip2region test program |"); \
|
||||
println("| Author: chenxin619315@gmail.com. |"); \
|
||||
println("| Type 'quit' to exit the program. |"); \
|
||||
println("+-------------------------------------+");
|
||||
|
||||
//read a line from a command line.
|
||||
static char *getLine( FILE *fp, char *__dst )
|
||||
{
|
||||
register int c;
|
||||
register char *cs;
|
||||
|
||||
cs = __dst;
|
||||
while ( ( c = getc( fp ) ) != EOF ) {
|
||||
if ( c == '\n' ) break;
|
||||
*cs++ = c;
|
||||
}
|
||||
*cs = '\0';
|
||||
|
||||
return ( c == EOF && cs == __dst ) ? NULL : __dst;
|
||||
}
|
||||
|
||||
static double getTime()
|
||||
{
|
||||
struct timeval tv;
|
||||
struct timezone tz;
|
||||
gettimeofday(&tv, &tz);
|
||||
|
||||
return (tv.tv_sec * 1000 + ((double)tv.tv_usec)/1000);
|
||||
}
|
||||
|
||||
int main( int argc, char **argv )
|
||||
{
|
||||
ip2region_entry ip2rEntry;
|
||||
datablock_entry datablock;
|
||||
char *dbFile = NULL, *algorithm = NULL;
|
||||
char line[256];
|
||||
uint_t (*func_ptr)(ip2region_t, char *, datablock_t);
|
||||
double s_time, c_time;
|
||||
memset(&datablock, 0x00, sizeof(datablock_entry));
|
||||
|
||||
if ( argc < 2 ) {
|
||||
printf("Usage: a.out [ip2region db file path] [algorithm]\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
dbFile = argv[1];
|
||||
algorithm = "B-tree";
|
||||
func_ptr = ip2region_btree_search_string;
|
||||
if ( argc >= 3 ) {
|
||||
if ( strcmp(argv[2], "binary") == 0 ) {
|
||||
algorithm = "File";
|
||||
func_ptr = ip2region_binary_search_string;
|
||||
}
|
||||
}
|
||||
|
||||
//create a new ip2rObj
|
||||
printf("+--initializing %s ... \n", algorithm);
|
||||
if ( ip2region_create(&ip2rEntry, dbFile) == 0 ) {
|
||||
println("Error: Fail to create the ip2region object\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
__PRINT_ABOUT__;
|
||||
|
||||
while ( 1 ) {
|
||||
print("ip2region>> ");
|
||||
getLine( stdin, line );
|
||||
if ( strlen(line) < 2 ) continue;
|
||||
if ( strcasecmp( line, "quit" ) == 0 ) {
|
||||
println("+--Bye!");
|
||||
break;
|
||||
}
|
||||
|
||||
s_time = getTime();
|
||||
func_ptr(&ip2rEntry, line, &datablock);
|
||||
c_time = getTime() - s_time;
|
||||
printf("%d|%s in %.5f millseconds\n", datablock.city_id, datablock.region, c_time);
|
||||
}
|
||||
|
||||
//destory the ip2rObj
|
||||
ip2region_destroy(&ip2rEntry);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -10,7 +10,7 @@ import (
|
|||
|
||||
const (
|
||||
INDEX_BLOCK_LENGTH = 12
|
||||
TOTAL_HEADER_LENGTH = 4096
|
||||
TOTAL_HEADER_LENGTH = 8192
|
||||
)
|
||||
|
||||
var err error
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
<property name="jars" value="${basedir}"/>
|
||||
<property name="sources" value="${basedir}/src"/>
|
||||
<property name="classes" value="${basedir}/classes"/>
|
||||
<property name="version" value="1.2"/>
|
||||
<property name="version" value="1.4"/>
|
||||
<property name="api" value="${basedir}/doc"/>
|
||||
<mkdir dir="${classes}"/>
|
||||
<mkdir dir="${api}"/>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,190 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>org.lionsoul</groupId>
|
||||
<artifactId>ip2region</artifactId>
|
||||
<version>1.4</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>ip2region</name>
|
||||
<url>http://gitee.com/lionsoul/ip2region/</url>
|
||||
<description>Open source internel address locator</description>
|
||||
|
||||
<licenses>
|
||||
<license>
|
||||
<name>The Apache Software License, Version 2.0</name>
|
||||
<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
|
||||
<distribution>repo</distribution>
|
||||
</license>
|
||||
</licenses>
|
||||
|
||||
<scm>
|
||||
<url>git@gitee.com:lionsoul/ip2region.git</url>
|
||||
<connection>scm:git:git@gitee.com:lionsoul/ip2region.git</connection>
|
||||
<developerConnection>scm:git:git@gitee.com:lionsoul/ip2region.git</developerConnection>
|
||||
</scm>
|
||||
|
||||
<developers>
|
||||
<developer>
|
||||
<id>lionsoul</id>
|
||||
<name>chenxin</name>
|
||||
<email>chenxin619315@gmail.com</email>
|
||||
</developer>
|
||||
</developers>
|
||||
|
||||
<issueManagement>
|
||||
<url>http://gitee.com/lionsoul/ip2region/issues</url>
|
||||
<system>gitee issues</system>
|
||||
</issueManagement>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>3.8.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-source-plugin</artifactId>
|
||||
<version>2.1.2</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>attach-sources</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>jar</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
<version>2.9</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>attach-javadocs</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>jar</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<additionalparam>${javadoc.opts}</additionalparam>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<failOnError>false</failOnError>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>1.4</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<transformers>
|
||||
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
|
||||
<mainClass>org.lionsoul.ip2region.test.TestSearcher</mainClass>
|
||||
</transformer>
|
||||
</transformers>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>java8-doclint-disabled</id>
|
||||
<activation>
|
||||
<jdk>[1.8,)</jdk>
|
||||
</activation>
|
||||
<properties>
|
||||
<javadoc.opts>-Xdoclint:none</javadoc.opts>
|
||||
</properties>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>release</id>
|
||||
<build>
|
||||
<plugins>
|
||||
<!-- Source -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-source-plugin</artifactId>
|
||||
<version>2.2.1</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>jar-no-fork</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<!-- Javadoc -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
<version>2.9.1</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>jar</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<additionalparam>${javadoc.opts}</additionalparam>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<!-- GPG -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-gpg-plugin</artifactId>
|
||||
<version>1.5</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>verify</phase>
|
||||
<goals>
|
||||
<goal>sign</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<distributionManagement>
|
||||
<snapshotRepository>
|
||||
<id>oss-parent</id>
|
||||
<url>https://oss.sonatype.org/content/repositories/snapshots/</url>
|
||||
</snapshotRepository>
|
||||
<repository>
|
||||
<id>oss-parent</id>
|
||||
<url>https://oss.sonatype.org/service/local/staging/deploy/maven2/</url>
|
||||
</repository>
|
||||
</distributionManagement>
|
||||
</profile>
|
||||
</profiles>
|
||||
|
||||
</project>
|
||||
|
|
@ -32,12 +32,12 @@ public class DbConfig
|
|||
}
|
||||
|
||||
this.totalHeaderSize = totalHeaderSize;
|
||||
this.indexBlockSize = 4096; //4 * 1024
|
||||
this.indexBlockSize = 8192; //4 * 2048
|
||||
}
|
||||
|
||||
public DbConfig() throws DbMakerConfigException
|
||||
{
|
||||
this(8192);
|
||||
this(8 * 2048);
|
||||
}
|
||||
|
||||
public int getTotalHeaderSize()
|
||||
|
|
@ -0,0 +1 @@
|
|||
node_modules/
|
||||
|
|
@ -1,3 +1,3 @@
|
|||
# nodejs 客户端
|
||||
|
||||
现已实现同步查询,异步查询未实现,用法参考 test.js
|
||||
现已实现同步查询,异步查询未实现,用法参考 ip2region.spec.js
|
||||
|
|
|
|||
|
|
@ -0,0 +1,29 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`binarySearch 1`] = `
|
||||
Object {
|
||||
"city": 2163,
|
||||
"region": "中国|华南|广东省|深圳市|阿里云",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`binarySearch 2`] = `
|
||||
Object {
|
||||
"city": 0,
|
||||
"region": "未分配或者内网IP|0|0|0|0",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`should query 1`] = `
|
||||
Object {
|
||||
"city": 2163,
|
||||
"region": "中国|华南|广东省|深圳市|阿里云",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`should query 2`] = `
|
||||
Object {
|
||||
"city": 0,
|
||||
"region": "未分配或者内网IP|0|0|0|0",
|
||||
}
|
||||
`;
|
||||
|
|
@ -18,7 +18,7 @@ var lastIndexPtr = 0;
|
|||
var superBlock = new Buffer(8);
|
||||
|
||||
var indexBlockLength = 12;
|
||||
var totalHeaderLength = 4096;
|
||||
var totalHeaderLength = 8192;
|
||||
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -0,0 +1,15 @@
|
|||
'use strict';
|
||||
|
||||
const ip = require('./ip2region');
|
||||
|
||||
const query = ip.create('../../data/ip2region.db');
|
||||
|
||||
it('should query', () => {
|
||||
expect(query.btreeSearchSync('120.24.78.68')).toMatchSnapshot();
|
||||
expect(query.btreeSearchSync('10.10.10.10')).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('binarySearch', () => {
|
||||
expect(query.binarySearchSync('120.24.78.68')).toMatchSnapshot();
|
||||
expect(query.binarySearchSync('10.10.10.10')).toMatchSnapshot();
|
||||
});
|
||||
|
|
@ -4,7 +4,7 @@
|
|||
"description": "ip database ",
|
||||
"main": "ip2region.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
"test": "jest"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
|
@ -20,5 +20,8 @@
|
|||
"bugs": {
|
||||
"url": "https://github.com/lionsoul2016/ip2region/issues"
|
||||
},
|
||||
"homepage": "https://github.com/lionsoul2016/ip2region"
|
||||
"homepage": "https://github.com/lionsoul2016/ip2region",
|
||||
"devDependencies": {
|
||||
"jest": "^19.0.2"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,15 +0,0 @@
|
|||
/**
|
||||
* test program
|
||||
* */
|
||||
|
||||
var Ip2region = require('./ip2region');
|
||||
|
||||
// 1. create a ip2region object
|
||||
var ipObj = Ip2region.create('../../data/ip2region.db');
|
||||
|
||||
// 2. use
|
||||
//console.log(ipObj.binarySearchSync("120.24.78.68"));
|
||||
console.log(ipObj.btreeSearchSync("120.24.78.68"));
|
||||
|
||||
// 3. destroy
|
||||
Ip2region.destroy(ipObj);
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -7,7 +7,7 @@
|
|||
*/
|
||||
|
||||
defined('INDEX_BLOCK_LENGTH') or define('INDEX_BLOCK_LENGTH', 12);
|
||||
defined('TOTAL_HEADER_LENGTH') or define('TOTAL_HEADER_LENGTH', 4096);
|
||||
defined('TOTAL_HEADER_LENGTH') or define('TOTAL_HEADER_LENGTH', 8192);
|
||||
|
||||
class Ip2Region
|
||||
{
|
||||
|
|
|
|||
|
|
@ -110,7 +110,7 @@ class Ip2Region(object):
|
|||
#pass the super block
|
||||
self.__f.seek(8)
|
||||
#read the header block
|
||||
b = self.__f.read(4086)
|
||||
b = self.__f.read(8192)
|
||||
#parse the header block
|
||||
sip = None
|
||||
ptr = None
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ def testSearch():
|
|||
llen = len(sys.argv)
|
||||
|
||||
if llen < 2:
|
||||
print "Usage: python ip2Region.py [ip2region db file] [alrogrithm]"
|
||||
print "Usage: python testSearcher.py [ip2region db file] [alrogrithm]"
|
||||
print "Algorithm: binary or b-tree"
|
||||
return 0
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,240 @@
|
|||
#-*- coding:utf-8 -*-
|
||||
"""
|
||||
" ip2region python seacher client module
|
||||
"
|
||||
" Author: koma<komazhang@foxmail.com>
|
||||
" Date : 2015-11-06
|
||||
"""
|
||||
import struct, io, socket, sys
|
||||
|
||||
class Ip2Region(object):
|
||||
__headerSip = []
|
||||
__headerPtr = []
|
||||
__f = None
|
||||
__sPtr = 0
|
||||
__indexLen = 0
|
||||
__dbBinStr = ''
|
||||
|
||||
def __init__(self, dbfile):
|
||||
self.initDatabase(dbfile)
|
||||
|
||||
def memorySearch(self, ip):
|
||||
"""
|
||||
" memory search method
|
||||
" param: ip
|
||||
"""
|
||||
if not ip.isdigit(): ip = self.ip2long(ip)
|
||||
|
||||
if self.__dbBinStr == '':
|
||||
self.__dbBinStr = self.__f.read() #read all the contents in file
|
||||
self.__sPtr = self.getLong(self.__dbBinStr, 0)
|
||||
endPtr = self.getLong(self.__dbBinStr, 4)
|
||||
self.__indexLen = endPtr - self.__sPtr
|
||||
|
||||
startPtr = self.__sPtr
|
||||
indexLen = self.__indexLen
|
||||
dbBinStr = self.__dbBinStr
|
||||
|
||||
l, h, mixPtr = (0, int(indexLen/12), 0)
|
||||
while l <= h:
|
||||
m = int((l+h)/2)
|
||||
ptr = startPtr + m*12
|
||||
|
||||
sip = self.getLong(dbBinStr, ptr)
|
||||
eip = self.getLong(dbBinStr, ptr+4)
|
||||
|
||||
if ip >= sip:
|
||||
if ip > eip:
|
||||
l = m + 1
|
||||
else:
|
||||
mixPtr = self.getLong(dbBinStr, ptr+8)
|
||||
break;
|
||||
else:
|
||||
h = m - 1
|
||||
|
||||
if mixPtr == 0: return "N2"
|
||||
|
||||
return self.returnData(mixPtr)
|
||||
|
||||
def binarySearch(self, ip):
|
||||
"""
|
||||
" binary search method
|
||||
" param: ip
|
||||
"""
|
||||
if not ip.isdigit(): ip = self.ip2long(ip)
|
||||
|
||||
if self.__indexLen < 1:
|
||||
self.__f.seek(0)
|
||||
b = self.__f.read(8)
|
||||
self.__sPtr = self.getLong(b, 0)
|
||||
endPtr = self.getLong(b, 4)
|
||||
self.__indexLen = endPtr - self.__sPtr
|
||||
|
||||
startPtr = self.__sPtr
|
||||
indexLen = self.__indexLen
|
||||
|
||||
self.__f.seek(startPtr)
|
||||
b = self.__f.read(indexLen+12)
|
||||
|
||||
l, h, mixPtr = (0, int(indexLen/12), 0)
|
||||
while l <= h:
|
||||
m = int((l+h)/2)
|
||||
ptr = startPtr + m*12
|
||||
self.__f.seek(ptr)
|
||||
|
||||
b = self.__f.read(12)
|
||||
sip = self.getLong(b, 0)
|
||||
eip = self.getLong(b, 4)
|
||||
|
||||
if ip >= sip:
|
||||
if ip > eip:
|
||||
l = m + 1
|
||||
else:
|
||||
mixPtr = self.getLong(b, 8)
|
||||
break;
|
||||
else:
|
||||
h = m - 1
|
||||
|
||||
if mixPtr == 0: return "N2"
|
||||
|
||||
return self.returnData(mixPtr)
|
||||
|
||||
def btreeSearch(self, ip):
|
||||
"""
|
||||
" b-tree search method
|
||||
" param: ip
|
||||
"""
|
||||
if not ip.isdigit(): ip = self.ip2long(ip)
|
||||
|
||||
if len(self.__headerSip) < 1:
|
||||
#pass the super block
|
||||
self.__f.seek(8)
|
||||
#read the header block
|
||||
b = self.__f.read(8192)
|
||||
#parse the header block
|
||||
sip = None
|
||||
ptr = None
|
||||
for i in range(0, len(b)-1, 8):
|
||||
sip = self.getLong(b, i)
|
||||
ptr = self.getLong(b, i+4)
|
||||
if ptr == 0:
|
||||
break
|
||||
self.__headerSip.append(sip)
|
||||
self.__headerPtr.append(ptr)
|
||||
|
||||
headerLen = len(self.__headerSip) - 1
|
||||
l, h, sptr, eptr = (0, headerLen, 0, 0)
|
||||
while l <= h:
|
||||
m = int((l+h)/2)
|
||||
|
||||
if ip == self.__headerSip[m]:
|
||||
if m > 0:
|
||||
sptr = self.__headerPtr[m-1]
|
||||
eptr = self.__headerPtr[m]
|
||||
break;
|
||||
else:
|
||||
sptr = self.__headerPtr[m]
|
||||
eptr = self.__headerPtr[m+1]
|
||||
break;
|
||||
|
||||
if ip > self.__headerSip[m]:
|
||||
if m == headerLen:
|
||||
sptr = self.__headerPtr[m-1]
|
||||
eptr = self.__headerPtr[m]
|
||||
break;
|
||||
elif ip < self.__headerSip[m+1]:
|
||||
sptr = self.__headerPtr[m]
|
||||
eptr = self.__headerPtr[m+1]
|
||||
break;
|
||||
|
||||
l = m + 1
|
||||
else:
|
||||
if m == 0:
|
||||
sptr = self.__headerPtr[m]
|
||||
eptr = self.__headerPtr[m+1]
|
||||
break;
|
||||
elif ip > self.__headerSip[m-1]:
|
||||
sptr = self.__headerPtr[m-1]
|
||||
eptr = self.__headerPtr[m]
|
||||
break;
|
||||
|
||||
h = m - 1
|
||||
|
||||
if sptr == 0: return "N1"
|
||||
|
||||
indexLen = eptr - sptr
|
||||
self.__f.seek(sptr)
|
||||
b = self.__f.read(indexLen + 12)
|
||||
|
||||
l, h, mixPtr = (0, int(indexLen/12), 0)
|
||||
while l <= h:
|
||||
m = int((l+h)/2)
|
||||
offset = m * 12
|
||||
|
||||
if ip >= self.getLong(b, offset):
|
||||
if ip > self.getLong(b, offset+4):
|
||||
l = m + 1
|
||||
else:
|
||||
mixPtr = self.getLong(b, offset+8)
|
||||
break;
|
||||
else:
|
||||
h = m - 1
|
||||
|
||||
if mixPtr == 0: return "N2"
|
||||
|
||||
return self.returnData(mixPtr)
|
||||
|
||||
def initDatabase(self, dbfile):
|
||||
"""
|
||||
" initialize the database for search
|
||||
" param: dbFile
|
||||
"""
|
||||
try:
|
||||
self.__f = io.open(dbfile, "rb")
|
||||
except IOError as e:
|
||||
print ("[Error]: ", e)
|
||||
sys.exit()
|
||||
|
||||
def returnData(self, dsptr):
|
||||
"""
|
||||
" get ip data from db file by data start ptr
|
||||
" param: dsptr
|
||||
"""
|
||||
dataPtr = dsptr & 0x00FFFFFF
|
||||
dataLen = (dsptr >> 24) & 0xFF
|
||||
|
||||
self.__f.seek(dataPtr)
|
||||
data = self.__f.read(dataLen)
|
||||
|
||||
return {
|
||||
"city_id": self.getLong(data, 0),
|
||||
"region" : data[4:]
|
||||
}
|
||||
|
||||
def ip2long(self, ip):
|
||||
_ip = socket.inet_aton(ip)
|
||||
|
||||
return struct.unpack("!L", _ip)[0]
|
||||
|
||||
def isip(self, ip):
|
||||
p = ip.split(".")
|
||||
|
||||
if len(p) != 4 : return False
|
||||
for pp in p:
|
||||
if not pp.isdigit() : return False
|
||||
if len(pp) > 3 : return False
|
||||
if int(pp) > 255 : return False
|
||||
|
||||
return True
|
||||
|
||||
def getLong(self, b, offset):
|
||||
if len( b[offset:offset+4] ) == 4:
|
||||
return struct.unpack('I', b[offset:offset+4])[0]
|
||||
|
||||
return 0
|
||||
|
||||
def close(self):
|
||||
self.__headerSip = None
|
||||
self.__headerPtr = None
|
||||
self.__f.close()
|
||||
self.__f = None
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
#-*- coding:utf-8 -*-
|
||||
"""
|
||||
" ip2region python seacher client module
|
||||
"
|
||||
" Autho: koma<komazhang@foxmail.com>
|
||||
" Date : 2015-11-06
|
||||
"""
|
||||
import struct, sys, os, time
|
||||
from ip2Region import Ip2Region
|
||||
|
||||
def testSearch():
|
||||
"""
|
||||
" ip2region test function
|
||||
"""
|
||||
llen = len(sys.argv)
|
||||
|
||||
if llen < 2:
|
||||
print ("Usage: python testSearcher.py [ip2region db file] [alrogrithm]")
|
||||
print ("Algorithm: binary or b-tree")
|
||||
return 0
|
||||
|
||||
dbFile = sys.argv[1]
|
||||
method = 1
|
||||
algorithm = "b-tree"
|
||||
|
||||
if (not os.path.isfile(dbFile)) or (not os.path.exists(dbFile)):
|
||||
print ("[Error]: Specified db file is not exists.")
|
||||
return 0
|
||||
|
||||
if llen > 2:
|
||||
algorithm = sys.argv[2]
|
||||
if algorithm == "binary":
|
||||
method = 2
|
||||
elif algorithm == "memory":
|
||||
method = 3
|
||||
|
||||
print ("initializing %s..."% algorithm)
|
||||
print ("+----------------------------------+")
|
||||
print ("| ip2region test program |")
|
||||
print ("| Author: chenxin619315@gmail.com. |")
|
||||
print ("| Type 'quit' or 'exit' to exit program |")
|
||||
print ("+----------------------------------+")
|
||||
|
||||
searcher = Ip2Region(dbFile);
|
||||
|
||||
while True:
|
||||
line = input("ip2region>> ")
|
||||
line = line.strip()
|
||||
|
||||
if line == "":
|
||||
print ("[Error]: Invalid ip address.")
|
||||
continue
|
||||
|
||||
if line == "quit" or line == "exit" :
|
||||
print ("[Info]: Thanks for your use, Bye.")
|
||||
break
|
||||
|
||||
if not searcher.isip(line):
|
||||
print ("[Error]: Invalid ip address.")
|
||||
continue
|
||||
|
||||
sTime = time.time() * 1000
|
||||
if method == 1:
|
||||
data = searcher.btreeSearch(line)
|
||||
elif method == 2:
|
||||
data = searcher.binarySearch(line)
|
||||
else:
|
||||
data = searcher.memorySearch(line)
|
||||
eTime = time.time() * 1000
|
||||
|
||||
if isinstance(data, dict):
|
||||
print ("%s|%s in %f millseconds" % (data["city_id"], data["region"].decode('utf-8'), eTime-sTime))
|
||||
else:
|
||||
print ("[Error]: ", data)
|
||||
|
||||
searcher.close()
|
||||
|
||||
if __name__ == "__main__":
|
||||
testSearch()
|
||||
256102
data/ip.merge.txt
256102
data/ip.merge.txt
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue