高春辉做了一个功德无量的事情,整理了一份高质量的 IP 库,http://tool.17mon.cn/ipdb.html,官方给出了 php 版本的解析函数,但是没有 c++ 的,倒是有人写了一个 php c 扩展,https://github.com/shukean/mon…,不过太长了。。
于是自己写了个 c++ 的
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <string>
#include <vector>
#include <fstream>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <assert.h>
class IP17MON {
public:
bool init(const std::string strPathToDataFile = "./17monipdb.dat") {
printf("Opening %s\n", strPathToDataFile.c_str());
std::ifstream ifDataFile(strPathToDataFile.c_str(), std::ios::binary);
if (ifDataFile.is_open() == false) {
printf("%m\n");
return false;
}
vecDataFile.assign(std::istreambuf_iterator<char>(ifDataFile), std::istreambuf_iterator<char>());
printf("Load %lu bytes success\n", vecDataFile.size());
unsigned int uiIndexLen = 0;
memcpy(&uiIndexLen, &vecDataFile[0], 4);
uiIndexLen = ntohl(uiIndexLen);
//printf("index len %u\n", uiIndexLen);
pIPIndex = &vecDataFile[4];
pIPData = &vecDataFile[uiIndexLen];
return true;
}
std::string find(const std::string strIP) {
if (strIP.empty()) {
return "";
}
struct sockaddr_in stSockAddrInet = {0};
if(inet_aton(strIP.c_str(), &stSockAddrInet.sin_addr) == 0) {
//printf("convert error\n");
return "";
}
unsigned int uiIP = ntohl(stSockAddrInet.sin_addr.s_addr);
//printf("ip %X\n", uiIP);
int iFirst = atoi(strIP.c_str());
int iStart = 0;
memcpy(&iStart, pIPIndex+(iFirst*4), 4);
//printf("start %d\n", iStart);
int iMaxComLen = pIPData - pIPIndex - 1024 - 4;
//printf("max compare len %d\n", iMaxComLen);
int iIndexOffset = -1;
unsigned char ucIndexLength = 0;
for (iStart = iStart * 8 + 1024; iStart < iMaxComLen; iStart += 8) {
unsigned int uiCurrIP = 0;
memcpy(&uiCurrIP, pIPIndex+iStart, 4);
uiCurrIP = ntohl(uiCurrIP);
//printf("curr %u looking %u\n", uiCurrIP, uiIP);
if (uiCurrIP >= uiIP) {
iIndexOffset = 0;
memcpy(&iIndexOffset, pIPIndex+iStart+4, 3);
memcpy(&ucIndexLength, pIPIndex+iStart+7, 1);
break;
}
}
//printf("index offset %u\n", iIndexOffset);
//printf("index length %u\n", ucIndexLength);
if (iIndexOffset == -1) {
return "";
}
std::string strRegion;
char *pRegion = pIPData + iIndexOffset - 1024;
strRegion.assign(pRegion, ucIndexLength);
//printf("str region %s\n", strRegion.c_str());
return strRegion;
}
private:
std::vector<char> vecDataFile;
char *pIPIndex, *pIPData;
};
int main() {
IP17MON oIP17MON;
assert(oIP17MON.init());
printf("%s -> %s\n", "14.17.22.40", oIP17MON.find("14.17.22.40").c_str());
return 0;
}
效率方面,在我 cpu e5300, ram 4g ddr2 800, os ubuntu 14.04 的机器上,大约是每秒 150 万次