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