commit
f6f71c74d6
|
|
@ -4,12 +4,23 @@
|
|||
*.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
|
||||
|
||||
/binding/c/testSearcher
|
||||
|
|
|
|||
85
README.md
85
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版本已支持),golang。
|
||||
已经集成的客户端有: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,43 +30,71 @@ 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文件**
|
||||
|
||||
### 6. 如何生成ip2region.db文件
|
||||
|
||||
从ip2region 1.2.2版本开始里面提交了一个dbMaker-{version}.jar的可以执行jar文件,用它来完成这个工作:
|
||||
* 1, 确保你安装好了java环境(不玩Java的童鞋就自己谷歌找找拉,临时用一用,几分钟的事情)
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
A = @
|
||||
CFLAGS= -g
|
||||
CC= gcc
|
||||
LIBS= -I./
|
||||
LDFLAGS=
|
||||
RM = rm -f
|
||||
MV = mv
|
||||
|
||||
testSearcher: ip2region.o
|
||||
$(A) $(CC) $(CFLAGS) $(LDFLAGS) $(LIBS) -o testSearcher testSearcher.c ip2region.c
|
||||
$(A) $(RM) ip2region.o testSearcher.o
|
||||
|
||||
db:
|
||||
$(A) cd ../../;\
|
||||
export LANG=en_US.UTF-8;\
|
||||
java -jar dbMaker-1.2.2.jar -src ./data/ip.merge.txt -region ./data/global_region.csv -dst .;\
|
||||
$(MV) ./ip2region.db ./binding/c/;\
|
||||
cd ./binding/c
|
||||
|
||||
all: db testSearcher
|
||||
|
||||
|
||||
clean:
|
||||
$(A) $(RM) testSearcher
|
||||
|
||||
.PHONY: all clean testSearcher db
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ typedef unsigned int uint_t;
|
|||
typedef unsigned long ulong_t;
|
||||
|
||||
#define INDEX_BLOCK_LENGTH 12
|
||||
#define TOTAL_HEADER_LENGTH 4096
|
||||
#define TOTAL_HEADER_LENGTH 8192
|
||||
|
||||
/*
|
||||
* ip2region properties struct
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
###golang 实现ip地址查询
|
||||
### golang 实现ip地址查询
|
||||
|
||||
####获取
|
||||
#### 获取
|
||||
|
||||
```
|
||||
go get github.com/mohong122/ip2region/binding/golang
|
||||
|
|
@ -9,7 +9,7 @@ go get github.com/mohong122/ip2region/binding/golang
|
|||
|
||||
|
||||
|
||||
####使用
|
||||
#### 使用
|
||||
|
||||
```golang
|
||||
|
||||
|
|
@ -38,7 +38,7 @@ func main() {
|
|||
|
||||
```
|
||||
|
||||
####返回对象
|
||||
#### 返回对象
|
||||
```golang
|
||||
type IpInfo struct {
|
||||
CityId int64
|
||||
|
|
|
|||
|
|
@ -9,8 +9,8 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
INDEX_BLOCK_LENGTH = 12
|
||||
TOTAL_HEADER_LENGTH = 4096
|
||||
INDEX_BLOCK_LENGTH = 12
|
||||
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()
|
||||
|
|
@ -18,7 +18,7 @@ var lastIndexPtr = 0;
|
|||
var superBlock = new Buffer(8);
|
||||
|
||||
var indexBlockLength = 12;
|
||||
var totalHeaderLength = 4096;
|
||||
var totalHeaderLength = 8192;
|
||||
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
262825
data/ip.merge.txt
262825
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