两种IP文件的读取

来源:互联网 发布:增加字段的sql语句 编辑:程序博客网 时间:2024/06/05 07:44

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;
}

 

原创粉丝点击