使用单例模式,并且只读一次库文件,在查询过程中均使用相关对字节复制,而不是使用指针的方式来避免“高并发”下的指针和内存问题
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PureIP
{
///<summary>
/// 提供从纯真IP数据库搜索IP信息的方法;
/// 感谢LumaQQ提供纯真IP数据库格式文档;
/// ----HeDaode 2007-12-28 四川教育学院
///</summary>
public class IPSearch
{
static byte[] ipstream = null;
static IPSearch IPsearch = null;
string ipfilePath;
static object o = new object();
long startPosition = 0; //文件头实际上是两个指针,分别指向了第一条索引和最后一条索引的绝对偏移
long endPosition = 0; //文件最后一条索引的位置
long count = 0; //总记录数
long[] ipArray = null; //将索引区域的ip转为long型数组存储
///<summary>
/// 构造函数
///</summary>
///<param name="ipfilePath">纯真IP数据库路径</param>
private IPSearch(string ipfilePath)
{
this.ipfilePath = ipfilePath;
ipstream = File.ReadAllBytes(ipfilePath);
if (ipstream == null) throw new Exception("ip库文件不存在");
ipArray = BlockToArray(ReadIPBlock()); //将索引区域的ip转为long型数组存储,方便后续根据ip查找其索引,使用完毕后指针会被放置到第一条索引的位置
}
public static IPSearch Init(string ipfilePath)
{
if (IPsearch == null)
{
lock (o)
{
if (IPsearch == null)
{
IPsearch = new IPSearch(ipfilePath);
}
}
}
return IPsearch;
}
///<summary>
/// 地理位置,包括国家和地区
///</summary>
public struct IPLocation
{
public string Ip { get; set; }
/// <summary>
/// 国家
/// </summary>
public string Country { get; set; }
/// <summary>
/// 地区(运营商)
/// </summary>
public string Area { get; set; }
/// <summary>
/// 省份
/// </summary>
public string Province
{
get
{
string[] s = Country.Split('省');
if (s != null && s.Length > 1)
{
return s[0]+"省";
}
else
{
if (Country.StartsWith("北京市"))
{
return "北京市";
}
else
if (Country.StartsWith("上海市"))
{
return "上海市";
}
else
if (Country.StartsWith("天津市"))
{
return "天津市";
}
else
if (Country.StartsWith("重庆市"))
{
return "重庆市";
}
//判断含有内蒙古
else if (Country.StartsWith("内蒙"))
{
return "内蒙古自治区";
}
else if (Country.StartsWith("新疆"))
{
return "新疆";
}
else if (Country.StartsWith("西藏"))
{
return "西藏自治区";
}
else if (Country.StartsWith("宁夏"))
{
return "宁夏回族自治区";
}
else if (Country.StartsWith("广西"))
{
return "广西壮族自治区";
}
else if (Country.StartsWith("香港"))
{
return "香港特别行政区";
}
else if (Country.StartsWith("澳门"))
{
return "澳门特别行政区";
}
}
return Country;
}
}
/// <summary>
/// 城市
/// </summary>
public string City
{
get
{
string[] s = Country.Split('省');
if (s != null && s.Length > 1)
{
return s[1];
}
else
{
return Country.Replace("行政区", "").Replace("自治区", "");
}
}
}
/// <summary>
/// 行政区编码(按县,市,省依次获取,获取即止)
/// </summary>
public string Code { get; set; }
}
///<summary>
/// 获取指定IP所在地理位置
///</summary>
///<param name="strIP">要查询的IP地址</param>
///<returns></returns>
public IPLocation GetIPLocation(string strIP)
{
long ip = IPToLong(strIP); //ip转为long类型
long beginPointer = startPosition; //指向第一条索引的位置
long offset = SearchIP(ipArray, 0, ipArray.Length - 1,ip) * 7 + 4; //根据ip得到索引,计算出其指针位置,二分法,在索引区每个ip记录占7个字节,ip本身占4个字节,这里得到ip对应的记录指针地址
//ipFile.Position += offset;//跳过起始IP
long startIndex= ReadLongX(beginPointer+offset,3) + 4;//跳过结束IP,读取国家偏移位置,并放置指针位置到 ??
IPLocation loc = new IPLocation();
loc.Ip = strIP;
int flag = ipstream[startIndex];//读取标志
startIndex++;
if (flag == 1)//表示国家和地区被转向
{
startIndex = ReadLongX(startIndex,3);
flag = ipstream[startIndex];//再读标志
startIndex++;
}
long countryOffset = startIndex;
loc.Country = ReadString(countryOffset,flag).Trim(); //国家
if (flag == 2)
{
startIndex = countryOffset + 3;
}
flag = ipstream[startIndex];
startIndex++;
loc.Area = ReadString(startIndex,flag).Trim(); //地区
//ipFile.Close();
//ipFile = null;
//ipFile.Position = beginPointer; //还原文件流指针位置
return loc;
}
///<summary>
/// 将字符串形式的IP转换位long
///</summary>
///<param name="strIP"></param>
///<returns></returns>
public long IPToLong(string strIP)
{
byte[] ip_bytes = new byte[8];
string[] strArr = strIP.Split('.');
for (int i = 0; i < 4; i++)
{
ip_bytes[i] = byte.Parse(strArr[3 - i]);
}
return BitConverter.ToInt64(ip_bytes, 0);
}
///<summary>
/// 将索引区字节块中的起始IP转换成Long数组
///</summary>
///<param name="ipBlock"></param>
long[] BlockToArray(byte[] ipBlock)
{
long[] ipArray = new long[ipBlock.Length / 7]; //ip记录个数
int ipIndex = 0;
byte[] temp = new byte[8]; //每个ip长度
for (int i = 0; i < ipBlock.Length; i += 7)
{
Array.Copy(ipBlock, i, temp, 0, 4);
ipArray[ipIndex] = BitConverter.ToInt64(temp, 0);
ipIndex++;
}
return ipArray;
}
///<summary>
/// 从IP数组中搜索指定IP并返回其索引
///</summary>
///<param name="ipArray">IP数组</param>
///<param name="start">指定搜索的起始位置</param>
///<param name="end">指定搜索的结束位置</param>
///<returns></returns>
int SearchIP(long[] ipArray, int start, int end ,long ip) //(二分法)
{
int middle = (start + end) / 2;
if (middle == start)
return middle;
else if (ip < ipArray[middle])
return SearchIP(ipArray, start, middle, ip);
else
return SearchIP(ipArray, middle, end, ip);
}
///<summary>
/// 读取IP文件中索引区块
///</summary>
///<returns>索引区块</returns>
byte[] ReadIPBlock()
{
startPosition = ReadLongX(0,4); //文件头实际上是两个指针,分别指向了第一条索引和最后一条索引的绝对偏移
endPosition = ReadLongX(4,4);
count = (endPosition - startPosition) / 7 + 1;//总记录数,每条索引长度为7个字节,前4个字节是起始IP地址,后三个字节就指向了IP记录
//ipFile.Position = startPosition; //指针定位到第一条索引的偏移位置
byte[] ipBlock = new byte[count * 7]; //ip索引区域所占的自己块
Array.Copy(ipstream, startPosition, ipBlock, 0, ipBlock.Length);
//ipstream.CopyTo(ipBlock,0,ipBlock.Length); //索引区域的记录值
//ipFile.Position = startPosition; //重置指针索引
return ipBlock;
}
///<summary>
/// 从IP文件中读取指定字节并转换位long
///</summary>
///<param name="index">读取索引位置</param>
///<param name="bytesCount">需要转换的字节数,主意不要超过8字节</param>
/// <returns></returns>
long ReadLongX(long index, int bytesCount)
{
byte[] _bytes = new byte[8];
//ipFile.Read(_bytes, 0, bytesCount);
Array.Copy(ipstream, index, _bytes, 0, bytesCount);
return BitConverter.ToInt64(_bytes, 0);
}
/// <summary>
/// 从IP文件中读取字符串
/// </summary>
/// <param name="index">读取开始位置</param>
/// <param name="flag">反向标志</param>
/// <returns></returns>
string ReadString(long index ,int flag)
{
long statrIndex = 0;
if (flag == 1 || flag == 2)//转向标志
statrIndex = ReadLongX(index,3);
else
statrIndex = index-1;
List<byte> list = new List<byte>();
byte b = (byte)ipstream[statrIndex];
statrIndex++;
while (b > 0)
{
list.Add(b);
b = (byte)ipstream[statrIndex];
statrIndex++;
}
return Encoding.Default.GetString(list.ToArray());
}
}
}
友情链接:
全部评论