优化读取纯真IP数据库QQWry.dat获取地区信息

时间:2022-06-01 22:33:49

改自HeDaode 2007-12-28的代码

将之改为从硬盘读取后文件后,将MemoryStream放到内存中,提高后续查询速度

    ///<summary>
/// 提供从纯真IP数据库搜索IP信息的方法;
///</summary>
public class IPSearch
{
FileStream fileStream = null;
static long[] ipArray = null;
static long initPosition = ;
long ip;
static MemoryStream ipFile =null;
///<summary>
/// 构造函数
///</summary>
public IPSearch()
{
if (ipArray == null)
{
lock ("ip")
{
if (ipArray == null)
{
fileStream = new FileStream("f:/files/qqwry.dat", FileMode.Open, FileAccess.Read, FileShare.Read);
ipArray = BlockToArray(ReadIPBlock());
initPosition = fileStream.Position;
fileStream.Position = ;
byte[] bytes = new byte[fileStream.Length];
fileStream.Read(bytes, , bytes.Length); ipFile = new MemoryStream(bytes);
fileStream.Close();
fileStream = null;
bytes = null;
}
}
}
ipFile.Position = initPosition;
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);//使.net core支持gb2312编码
} /// <summary>
/// 更新ip数据文件后,对内存中的流进行更新
/// </summary>
public void ClearStream()
{
ipFile.Close();
ipFile = null;
ipArray = null;
initPosition = ;
} ///<summary>
/// 获取指定IP所在地理位置
///</summary>
///<param name="strIP">要查询的IP地址</param>
///<returns></returns>
public string GetIPLocation(string strIP)
{
ip = IPToLong(strIP); long offset = SearchIP(ipArray, , ipArray.Length - ) * + ;
ipFile.Position += offset;//跳过起始IP
ipFile.Position = ReadLongX() + ;//跳过结束IP //IPLocation loc = new IPLocation();
int flag = ipFile.ReadByte();//读取标志
if (flag == )//表示国家和地区被转向
{
ipFile.Position = ReadLongX();
flag = ipFile.ReadByte();//再读标志
}
//long countryOffset = ipFile.Position;
string country = ReadString(flag); //if (flag == 2)
//{
// ipFile.Position = countryOffset + 3;
//}
//flag = ipFile.ReadByte();
//loc.area = ReadString(flag); //ipFile.Close();
//ipFile = null;
if (!country.Contains("市"))
{
return null;
}
if (!country.Contains("省"))
{
return country;
}
return country.Split("省")[];
}
///<summary>
/// 将字符串形式的IP转换位long
///</summary>
///<param name="strIP"></param>
///<returns></returns>
public long IPToLong(string strIP)
{
byte[] ip_bytes = new byte[];
string[] strArr = strIP.Split(new char[] { '.' });
for (int i = ; i < ; i++)
{
ip_bytes[i] = byte.Parse(strArr[ - i]);
}
return BitConverter.ToInt64(ip_bytes, );
}
///<summary>
/// 将索引区字节块中的起始IP转换成Long数组
///</summary>
///<param name="ipBlock"></param>
long[] BlockToArray(byte[] ipBlock)
{
long[] ipArray = new long[ipBlock.Length / ];
int ipIndex = ;
byte[] temp = new byte[];
for (int i = ; i < ipBlock.Length; i += )
{
Array.Copy(ipBlock, i, temp, , );
ipArray[ipIndex] = BitConverter.ToInt64(temp, );
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)
{
int middle = (start + end) / ;
if (middle == start)
return middle;
else if (ip < ipArray[middle])
return SearchIP(ipArray, start, middle);
else
return SearchIP(ipArray, middle, end);
}
///<summary>
/// 读取IP文件中索引区块
///</summary>
///<returns></returns>
byte[] ReadIPBlock()
{
long startPosition = StreamReadLongX();
long endPosition = StreamReadLongX();
long count = (endPosition - startPosition) / + ;//总记录数
fileStream.Position = startPosition;
byte[] ipBlock = new byte[count * ];
fileStream.Read(ipBlock, , ipBlock.Length);
fileStream.Position = startPosition;
return ipBlock;
}
///<summary>
/// 从IP文件中读取指定字节并转换位long
///</summary>
///<param name="bytesCount">需要转换的字节数,主意不要超过8字节</param>
///<returns></returns>
long StreamReadLongX(int bytesCount)
{
byte[] _bytes = new byte[];
fileStream.Read(_bytes, , bytesCount);
return BitConverter.ToInt64(_bytes, );
} ///<summary>
/// 从IP文件中读取指定字节并转换位long
///</summary>
///<param name="bytesCount">需要转换的字节数,主意不要超过8字节</param>
///<returns></returns>
long ReadLongX(int bytesCount)
{
byte[] _bytes = new byte[];
ipFile.Read(_bytes, , bytesCount); return BitConverter.ToInt64(_bytes, );
}
///<summary>
/// 从IP文件中读取字符串
///</summary>
///<param name="flag">转向标志</param>
///<returns></returns>
string ReadString(int flag)
{
if (flag == || flag == )//转向标志
ipFile.Position = ReadLongX();
else
ipFile.Position -= ; List<byte> list = new List<byte>();
byte b = (byte)ipFile.ReadByte();
while (b > )
{
list.Add(b);
b = (byte)ipFile.ReadByte();
} return Encoding.GetEncoding("gb2312").GetString(list.ToArray());
}
}

测试:

        [HttpPost("pt")]
public IActionResult PayTest([FromQuery]string ip)
{
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
IPSearch iPSearch = new IPSearch();
string result = iPSearch.GetIPLocation(ip);
stopwatch.Stop();
return Ok(new { stopwatch.Elapsed,result});
}

第一次运行稍慢,以测试机的配置需要25~50毫秒之间

之后每次运行均在1毫秒以内

优化读取纯真IP数据库QQWry.dat获取地区信息