add support for C#
This commit is contained in:
parent
39555fc266
commit
b65d8206ba
16
README.md
16
README.md
|
|
@ -18,10 +18,11 @@ ip2region - 最自由的ip地址查询库,ip到地区的映射库,提供Bina
|
||||||
|
|
||||||
### 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算法:
|
客户端/binary算法/b-tree算法/Memory算法:
|
||||||
|
c#/0.x毫秒/0.x毫秒/0.x毫秒
|
||||||
java/0.x毫秒/0.x毫秒/0.1x毫秒 (使用RandomAccessFile)
|
java/0.x毫秒/0.x毫秒/0.1x毫秒 (使用RandomAccessFile)
|
||||||
php/0.x毫秒/0.1x毫秒/0.1x毫秒
|
php/0.x毫秒/0.1x毫秒/0.1x毫秒
|
||||||
c/0.0x毫秒/0.0x毫秒/0.00x毫秒(b-tree算法基本稳定在0.02x毫秒级别)
|
c/0.0x毫秒/0.0x毫秒/0.00x毫秒(b-tree算法基本稳定在0.02x毫秒级别)
|
||||||
|
|
@ -37,8 +38,21 @@ maven仓库地址:
|
||||||
<version>1.4</version>
|
<version>1.4</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
```
|
```
|
||||||
|
nuget安装命令
|
||||||
|
```shell
|
||||||
|
Install-Package IP2Region
|
||||||
|
```
|
||||||
|
|
||||||
### 5. 测试程序:
|
### 5. 测试程序:
|
||||||
|
c#:
|
||||||
|
```shell
|
||||||
|
cd IP2Region_ConsoleTest
|
||||||
|
dotnet run
|
||||||
|
example result:
|
||||||
|
请输入IP地址:
|
||||||
|
36.149.160.55
|
||||||
|
```
|
||||||
|
|
||||||
java:
|
java:
|
||||||
```shell
|
```shell
|
||||||
cd binding/java
|
cd binding/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>
|
||||||
Loading…
Reference in New Issue