17mon IP 解析代码 C++ 版

高春辉做了一个功德无量的事情,整理了一份高质量的 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 万次

Leave a Reply

Your email address will not be published. Required fields are marked *