参看了,LumaQQ 作者对数据库文件分析的文章,和实现代码,改写的C#.NET版本,感谢原作者。
/*******************************************
* 说明:查询纯真IP数据库,数据库来自CZ88.NET(纯真网络),感谢 LumaQQ 作者:http://lumaqq.linuxsir.org/article/qqwry_format_detail.html
* 作者:孙宇
* 日期:2011/11/08
/*******************************************/
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Text;
namespace CZIPDemo
{
///
/// 存储地区的结构
///
public struct stLocation
{
///
/// 未使用
///
public string Ip;
///
/// 国家名
///
public string Contry;
///
/// 城市名
///
public string City;
}
///
/// 纯真IP数据库查询辅助类
///
public static class QqwryHelper
{
#region 成员变量
private const byte REDIRECT_MODE_1 = 0x01;//名称存储模式一
private const byte REDIRECT_MODE_2 = 0x02;//名称存储模式二
private const int IP_RECORD_LENGTH = 7; //每条索引的长度
private static long beginIndex = 0;//索引开始
private static long endIndex = 0;//索引结束
private static stLocation loc = new stLocation() { City = "未知城市", Contry = "未知国家" };
private static Stream fs;
#endregion
#region 私有成员函数
///
/// 在索引区查找指定IP对应的记录区地址
///
/// 字节型IP
///
private static long SearchIpIndex(byte[] _ip)
{
long index = 0;
byte[] nextIp = new byte[4];
ReadIp(beginIndex, ref nextIp);
int flag = CompareIp(_ip, nextIp);
if (flag == 0) return beginIndex;
else if (flag < 0) return -1;
for (long i = beginIndex, j = endIndex; i < j; )
{
index = GetMiddleOffset(i, j);
ReadIp(index, ref nextIp);
flag = CompareIp(_ip, nextIp);
if (flag == 0) return ReadLong(index + 4, 3);
else if (flag > 0) i = index;
else if (flag < 0)
{
if (index == j)
{
j -= IP_RECORD_LENGTH;
index = j;
}
else
{
j = index;
}
}
}
index = ReadLong(index + 4, 3);
ReadIp(index, ref nextIp);
flag = CompareIp(_ip, nextIp);
if (flag <= 0) return index;
else return -1;
}
///
/// 获取两个索引的中间位置
///
/// 索引1
/// 索引2
///
private static long GetMiddleOffset(long begin, long end)
{
long records = (end - begin) / IP_RECORD_LENGTH;
records >>= 1;
if (records == 0) records = 1;
return begin + records * IP_RECORD_LENGTH;
}
///
/// 读取记录区的地区名称
///
/// 位置
///
private static string ReadString(long offset)
{
fs.Position = offset;
byte b = (byte)fs.ReadByte();
if (b == REDIRECT_MODE_1 || b == REDIRECT_MODE_2)
{
long areaOffset = ReadLong(offset + 1, 3);
if (areaOffset == 0)
return "未知地区";
else fs.Position = areaOffset;
}
else
{
fs.Position = offset;
}
List buf = new List();
int i = 0;
for (i = 0, buf.Add((byte)fs.ReadByte()); buf[i] != (byte)(0); ++i, buf.Add((byte)fs.ReadByte())) ;
if (i > 0) return Encoding.Default.GetString(buf.ToArray(), 0, i);
else return "";
}
///
/// 从自定位置读取指定长度的字节,并转换为big-endian字节序(数据源文件为little-endian字节序)
///
/// 开始读取位置
/// 读取长度
///
private static long ReadLong(long offset, int length)
{
long ret = 0;
fs.Position = offset;
for (int i = 0; i < length; i++)
{
ret |= ((fs.ReadByte() << (i * 8)) & (0xFF * ((int)Math.Pow(16, i * 2))));
}
return ret;
}
///
/// 从指定位置处读取一个IP
///
/// 指定的位置
/// 保存IP的缓存区
private static void ReadIp(long offset, ref byte[] _buffIp)
{
fs.Position = offset;
fs.Read(_buffIp, 0, _buffIp.Length);
for (int i = 0; i < _buffIp.Length / 2; i++)
{
byte temp = _buffIp[i];
_buffIp[i] = _buffIp[_buffIp.Length - i - 1];
_buffIp[_buffIp.Length - i - 1] = temp;
}
}
///
/// 比较两个IP是否相等,1:IP1大于IP2,-1:IP1小于IP2,0:IP1=IP2
///
/// IP1
/// IP2
///
private static int CompareIp(byte[] _buffIp1, byte[] _buffIp2)
{
if (_buffIp1.Length > 4 || _buffIp2.Length > 4) throw new Exception("指定的IP无效。");
for (int i = 0; i < 4; i++)
{
if ((_buffIp1[i] & 0xFF) > (_buffIp2[i] & 0xFF)) return 1;
else if ((_buffIp1[i] & 0xFF) < (_buffIp2[i] & 0xFF)) return -1;
}
return 0;
}
///
/// 从指定的地址获取区域名称
///
///
private static void GetAreaName(long offset)
{
fs.Position = offset + 4;
long flag = fs.ReadByte();
long contryIndex = 0;
if (flag == REDIRECT_MODE_1)
{
contryIndex = ReadLong(fs.Position, 3);
fs.Position = contryIndex;
flag = fs.ReadByte();
if (flag == REDIRECT_MODE_2) //是否仍然为重定向
{
loc.Contry = ReadString(ReadLong(fs.Position, 3));
fs.Position = contryIndex + 4;
}
else
{
loc.Contry = ReadString(contryIndex);
}
loc.City = ReadString(fs.Position);
}
else if (flag == REDIRECT_MODE_2)
{
contryIndex = ReadLong(fs.Position, 3);
loc.Contry = ReadString(contryIndex);
loc.City = ReadString(contryIndex + 3);
}
else
{
loc.Contry = ReadString(offset + 4);
loc.City = ReadString(fs.Position);
}
}
#endregion
#region 公有成员函数
///
/// 加载数据库文件到缓存
///
/// 数据库文件地址
///
public static void Init(string path)
{
fs = new FileStream(path, FileMode.Open);
}
///
/// 根据IP获取区域名
///
/// 指定的IP
///
public static stLocation GetLocation(string ip)
{
IPAddress ipAddress = null;
if (!IPAddress.TryParse(ip, out ipAddress)) throw new Exception("无效的IP地址。");
byte[] buff_local_ip = ipAddress.GetAddressBytes();
beginIndex = ReadLong(0, 4);
endIndex = ReadLong(4, 4);
long offset = SearchIpIndex(buff_local_ip);
if (offset != -1)
{
GetAreaName(offset);
}
loc.Contry = loc.Contry.Trim();
loc.City = loc.City.Trim().Replace("CZ88.NET", "");
return loc;
}
///
/// 释放资源
///
public static void Dispose()
{
fs.Dispose();
}
#endregion
}
}