1、DBF IP数据库文件的读取
早期的IP数据库文件实际上是一个dbf文件,它是foxpro的数据库文件格式,除了可以用数据库的方式读取外,还可以用直接文件读取的方式读取。它的文件格式为(内容为全文转载):
“我分析了这个文件的格式,目前如下结论:
格式如下:
A。文件头,共8字节
B。若干条记录的结束地址+国家和区域
C。按照从小到大排列的若干条起始地址+结束地址偏移,定长,7字节
D。所有的IP都是用4字节整数记录的,并且遵照Intel次序,高位在后,低位在前。
E。所有偏移量都是绝对偏移,就是从文件最开头计算。
F。除了文件头用了两个4字节偏移,其余偏移量都用3字节。
G。所有的偏移量也是低位在前,高位在后
H。采用了一些字符串压缩技术
1。文件头,共8字节
FirstStartIpOffset:4 第一个起始IP的绝对偏移
LastStartIpOffset:4 最后一个起始IP的绝对偏移
2。起始地址+结束地址偏移记录区
每条记录7字节,按照起始地址从小到大排列
StartIp:4 起始地址,整数形式的IP
EndIpOffset:3 结束地址绝对偏移
3。结束地址+国家+区域记录区
EndIP:4
国家+区域记录:不定长
4。国家+区域记录,有几种形式
4.1。
国家字符串,以 0x0 结束
区域字符串,以 0x0 结束
4.2。
Flag:1 标识取值: 0x1,后面没有Local记录
0x2,后面还有Local记录
sCountryOffset:3 实际的字符串要去这个偏移位置去找
LocalRec:不定长,可选 根据Flag取值而定。这个记录也类似Country,可能采用压缩
4.3 LocalRec结构一
flag:1 还不是十分了解这个flag含义,取值 0x1 or 0x2
sLocalOffset:3
4.4 LocalRec结构二
sLocal:不定长 普通的C风格字符串
注意:sCountryOffset指向的位置可能依然是4.2格式的,不知道为什么这样设计。
Flag取0x1时,sCountryOffset指向的位置可能是Flag为0x2,这时,LocalRec也在这里寻找。
现在不明白当记录Local的位置遇到0x2的标志意味着什么。
在qqwry.dat中,似乎存在一些错误。
个别的记录Local会被写为:
0x2,0x0,0x0,0x0
根据规则,应该到文件最开头去寻找,可是,文件最开头显然不是记录这些的。
愿意跟我探讨,请到我的论坛
http://strongc.51.net/d2x/bbs/”
代码为:
CFile wryFile;
long lItemCount = 0l; // 文件中的项目总数
int nItemLength = 0, // 读取文件数据起始地址
nStartPos = 0; // 读取文件每项数据的长度
CString rsCountry, // 所在地的国家名
rsLocal, // 所在地的地名
rsStartIP, // 找到的IP开始范围
rsEndIP, // 找到的IP结束范围
rsThank; // 感谢人
if(!wryFile.Open(strFilePath, CFile::modeRead))
{
// 没有找到文件
MessageBox("不能打开IP数据库文件","出错提示",MB_OK);
return;
}
// 读取文件头
wryFile.Seek(4, CFile::begin);
wryFile.Read(&lItemCount, 4); // 读取文件中的项目总数
wryFile.Read(&nStartPos, 2); // 读取文件数据起始地址
wryFile.Read(&nItemLength, 2); // 读取文件每项数据的长度
// 开始二分法搜索
BOOL bFind = FALSE;
INT nItemStart = 1,
nItemEnd = lItemCount,
nItem = 0, nLastItem,
nPos;
struct ITEM_STRUCT
{
CHAR achStartIP[16];
CHAR achEndIP[16];
CHAR achCountry[14];
CHAR achLocal[47];
CHAR achThank[12];
} item;
//将IP规范化为***.***.***.***形式,因为二分查找时202.114.6.211将被理解为202.114.600.211
if(!NormalizeIP(strIP))
{
MessageBox(NULL,"不是有效的IP地址!","错误",MB_OK|MB_ICONWARNING);
return;
}
do
{
nLastItem = nItem;
nItem = (nItemEnd + nItemStart) / 2;
// 计算nItem在文件中的位置
nPos = nStartPos + nItem * nItemLength + 1;
// 读入该项数据
wryFile.Seek(nPos, CFile::begin);
wryFile.Read(&item, sizeof(item));
// 整理数据
item.achStartIP[15] = '/0';
item.achEndIP[15] = '/0';
item.achCountry[13] = '/0';
item.achLocal[46] = '/0';
item.achThank[11] = '/0';
// 判断当前的项目是否在该IP范围内
if(strIP.Compare(item.achStartIP) < 0)
{ // 如果要找的项目小于当前项目的开始IP
nItemEnd = nItem;
continue;
}
if(strIP.Compare(item.achEndIP) > 0)
{ // 如果要找的项目大于当前项目的结束IP
nItemStart = nItem;
continue;
}
// 找到了
rsCountry = item.achCountry;
rsLocal = item.achLocal;
rsStartIP = item.achStartIP;
rsEndIP = item.achEndIP;
rsThank = item.achThank;
bFind = TRUE;
break;
}while (nItem != nLastItem);
wryFile.Close();
if(bFind)
{
//找到了
//MessageBox(NULL,rsCountry,"国家",MB_OK);
//MessageBox(NULL,rsLocal,"地区",MB_OK);
//MessageBox(NULL,rsStartIP,"起始IP",MB_OK);
//MessageBox(NULL,rsEndIP,"结束IP",MB_OK);
//MessageBox(NULL,rsThank,"特别感谢",MB_OK);
}
else
{
MessageBox(NULL,"没有找到此IP!","提示",MB_OK);
}
2、新版纯真(CZ88.NET)IP数据库的读取
上面这种数据库文件数据压缩率很低,太占空间,纯真CZ88.net的IP数据是前不久比较流行的。据说新出的数据库压缩率更高,由于目前没有这方面的功能需求,就没有做过了。
纯真IP数据库的文件格式我没有找到,但在网上确找到过一段php查询的代码,我不懂php,但它看起来似乎有点像C,于是我把它改成了C++代码:
class CReadQQWry
{
public:
CReadQQWry();
CReadQQWry(CString &strFilePath);
virtual ~CReadQQWry();
//IP查询接口
CString QueryIP(CString &strIP);
protected:
CFile file;
//第一个起始IP的绝对偏移
DWORD FirstStartIpOffset;
//最后一个起始IP的绝对偏移
DWORD LastStartIpOffset;
DWORD m_dwStartIP;//起始IP
DWORD m_dwEndIP;//结束IP
DWORD m_dwEndIPOffset;//结束IP的偏移量,紧跟在起始IP后面
CString m_strAddr;//地址
BYTE m_bCountryFlag;//国家标记,1表示后面没有Local记录,2表示后面有Local记录
//读取指定索引的一行信息
void GetInfo(int nIndex);
CString GetFlagString(DWORD offset);
//读取字符串,以0结束
CString GetString(DWORD offset);
};
CReadQQWry::CReadQQWry()
{
}
CReadQQWry::CReadQQWry(CString &strPath)
{
file.Open(strPath, CFile::modeRead | CFile::shareDenyNone);
}
CReadQQWry::~CReadQQWry()
{
file.Close();
}
CString CReadQQWry::QueryIP(CString &strIP)
{
file.Read(&FirstStartIpOffset, sizeof(FirstStartIpOffset));//第一个IP的绝对偏移
file.Read(&LastStartIpOffset, sizeof(LastStartIpOffset));//最后一个IP的绝对偏移
DWORD dwIP = inet_addr(strIP);
int nItemCount = (LastStartIpOffset - FirstStartIpOffset) / 7,
nItemStart = 0,
nItemEnd = nItemCount,
nCurItem = 0;
BOOL bFound = FALSE;
DWORD dwLastStartIP = 0, dwLastEndIP = 0;
do
{
nCurItem = (nItemStart + nItemEnd) / 2;
GetInfo(nCurItem);
if(m_dwStartIP == dwLastStartIP && m_dwEndIP == dwLastEndIP)
{
// 检索到重复查询,如果再继续下去,会出现死循环
break;
}
dwLastStartIP = m_dwStartIP;
dwLastEndIP = m_dwEndIP;
if(dwIP < m_dwStartIP)
{
nItemEnd = nCurItem;
continue;
}
if(dwIP > m_dwEndIP)
{
nItemStart = nCurItem;
continue;
}
//找到了
bFound = TRUE;
break;
} while(nCurItem <= nItemEnd);
if(bFound)
return m_strAddr;
else
return CString(_T("未找到相关记录"));
}
void CReadQQWry::GetInfo(int nIndex)
{
CString strCountry, strLocal;
DWORD dwOffset;//指定索引起始IP的绝对偏移量
dwOffset = FirstStartIpOffset + nIndex * 7;
BYTE buf[7];
file.Seek(dwOffset, CFile::begin);
file.Read(buf, 7);
m_dwStartIP = buf[0] | buf[1] << 8 | buf[2] << 16 | buf[3] << 24;
m_dwEndIPOffset = buf[4] | buf[5] << 8 | buf[6] << 16;
file.Seek(m_dwEndIPOffset, CFile::begin);
memset(buf, 0, 7);
file.Read(buf, 4);
m_dwEndIP = buf[0] | buf[1] << 8 | buf[2] << 16 | buf[3] << 24;
file.Read(&m_bCountryFlag, 1);
switch(m_bCountryFlag)
{
case 1:
case 2:
strCountry = GetFlagString(m_dwEndIPOffset + 4);
strLocal = (m_bCountryFlag == 1) ? "" : GetFlagString(m_dwEndIPOffset + 8);
break;
default://后面直接跟的是conutry + local
strCountry = GetFlagString(m_dwEndIPOffset + 4);
strLocal = GetFlagString(file.GetPosition());
break;
}
m_strAddr = strCountry + strLocal;
}
CString CReadQQWry::GetFlagString(DWORD offset)
{
CString strSec;
BYTE flag;
BYTE buf[3];
while(TRUE)
{
file.Seek(offset, CFile::begin);
file.Read(&flag, 1);
if(flag == 1 || flag == 2)
{
file.Read(buf, 3);
if(flag == 2)
{
m_bCountryFlag = 2;
m_dwEndIPOffset = offset - 4;
}
offset = buf[0] | buf[1] << 8 | buf[2] << 16;
}
else
break;
}
if(offset < 12)
return CString("");
return GetString(offset);
}
CString CReadQQWry::GetString(DWORD offset)
{
CString strSec;
file.Seek(offset, CFile::begin);
//逐个字节读取,直到遇到结束符'/0'
char ch = 0;
while(TRUE)
{
file.Read(&ch, 1);
if(ch == '/0')
break;
strSec += ch;
}
return strSec;
}