读取纯真IP数据库C++源代码

头文件

#pragma once;
#include <string>

const int INDEX_LENGTH = 7;        // 一个索引包含4字节的起始IP和3字节的IP记录偏移,共7字节
const int IP_LENGTH = 4;
const int OFFSET_LENGTH = 3;

enum {
 REDIRECT_MODE_1 = 0x01,    // 重定向模式1 偏移量后无地区名
 REDIRECT_MODE_2 = 0x02,    // 重定向模式2 偏移量后有地区名
};

class CIpFinder
{
public:
    CIpFinder();
    CIpFinder(const char* pszFileName);
    ~CIpFinder();

    // 获取ip国家名、地区名
    void GetAddressByIp(unsigned long ipValue, std::string& strCountry, std::string& strLocation) const;
    void GetAddressByIp(const char* pszIp, std::string& strCountry, std::string& strLocation) const;
    void GetAddressByOffset(unsigned long ulOffset, std::string& strCountry, std::string& strLocation) const;

    unsigned long GetString(std::string& str, unsigned long indexStart) const;
    unsigned long GetValue3(unsigned long indexStart) const;
    unsigned long GetValue4(unsigned long indexStart) const;

    // 转换
    unsigned long IpString2IpValue(const char *pszIp) const;
    void IpValue2IpString(unsigned long ipValue, char* pszIpAddress, int nMaxCount) const;
    bool IsRightIpString(const char* pszIp) const;

    // 输出数据
    unsigned long OutputData(const char* pszFileName, unsigned long ulIndexStart = 0, unsigned long ulIndexEnd = 0) const;
    unsigned long OutputDataByIp(const char* pszFileName, unsigned long ipValueStart, unsigned long ipValueEnd) const;
    unsigned long OutputDataByIp(const char* pszFileName, const char* pszStartIp, const char* pszEndIp) const;

    unsigned long SearchIp(unsigned long ipValue, unsigned long indexStart = 0, unsigned long indexEnd = 0) const;
    unsigned long SearchIp(const char* pszIp, unsigned long indexStart = 0, unsigned long indexEnd = 0) const;

    bool Open(const char* pszFileName);
private:
    FILE *m_fpIpDataFile;            // IP数据库文件
    unsigned long m_indexStart;    // 起始索引偏移
    unsigned long m_indexEnd;        // 结束索引偏移
};

实现文件

#include "ip.h"
// ============================================================================
// ==============================================================================
CIpFinder::CIpFinder()
{
}

// ============================================================================
// ==============================================================================
CIpFinder::CIpFinder(const char *pszFileName)
{
    this->Open(pszFileName);
}

// ============================================================================
//    打开数据库文件
// ==============================================================================
bool CIpFinder::Open(const char *pszFileName)
{
    m_fpIpDataFile = fopen(pszFileName, "rb");
    if (!m_fpIpDataFile) {
        return false;
    }

    // IP头由两个十六进制4字节偏移量构成,分别为索引开始,和索引结束
    m_indexStart = this->GetValue4(0);
    m_indexEnd = this->GetValue4(4);
    return true;
}

// ============================================================================
// ==============================================================================
CIpFinder::~CIpFinder()
{
    fclose(m_fpIpDataFile);
}

// ============================================================================
//    根据IP地址字符串返回其十六进制值(4字节)
// ============================================================================
unsigned long CIpFinder::IpString2IpValue(const char *pszIp) const
{
    if (!this->IsRightIpString(pszIp)) {
        return 0;
    }
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    int nNum = 0;            // 每个段数值
    const char *pBeg = pszIp;
    const char *pPos = NULL;
    unsigned long ulIp = 0; // 整个IP数值
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    pPos = strchr(pszIp, '.');
    while (pPos != NULL) {
        nNum = atoi(pBeg);
        ulIp += nNum;
        ulIp *= 0x100;
        pBeg = pPos + 1;
        pPos = strchr(pBeg, '.');
    }

    nNum = atoi(pBeg);
    ulIp += nNum;
    return ulIp;
}

// ============================================================================
//    根据ip值获取字符串(由点分割)
// ============================================================================
void CIpFinder::IpValue2IpString(unsigned long ipValue,
                                 char *pszIpAddress,
                                 int nMaxCount) const
{
    if (!pszIpAddress) {
        return;
    }

    _snprintf(pszIpAddress, nMaxCount, "%d.%d.%d.%d", (ipValue & 0xFF000000) >> 24,
              (ipValue & 0x00FF0000) >> 16, (ipValue & 0x0000FF00) >> 8,ipValue & 0x000000FF);
    pszIpAddress[nMaxCount - 1] = 0;
}

// ============================================================================
//    根据指定IP(十六进制值),返回其在索引段中的位置(索引)
//    ulIndexStart和ulIndexEnd可以指定搜索范围 均为0表示搜索全部
// ============================================================================
unsigned long CIpFinder::SearchIp(unsigned long ipValue,
                                  unsigned long indexStart,
                                  unsigned long indexEnd) const
{
    if (!m_fpIpDataFile) {
        return 0;
    }

    if (0 == indexStart) {
        indexStart = m_indexStart;
    }

    if (0 == indexEnd) {
        indexEnd = m_indexEnd;
    }

    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    unsigned long indexLeft = indexStart;
    unsigned long indexRight = indexEnd;

    // 先除后乘是为了保证mid指向一个完整正确的索引
    unsigned long indexMid = (indexRight - indexLeft) / INDEX_LENGTH / 2 * INDEX_LENGTH + indexLeft;

    // 起始Ip地址(如172.23.0.0),他和Ip记录中的Ip地址(如172.23.255.255)构成一个Ip范围,在这个范围内的Ip都可以由这条索引来获取国家、地区
    unsigned long ulIpHeader = 0;
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    do
    {
        ulIpHeader = this->GetValue4(indexMid);
        if (ipValue < ulIpHeader) {
            indexRight = indexMid;
            indexMid = (indexRight - indexLeft) /    INDEX_LENGTH / 2 * INDEX_LENGTH + indexLeft;
        } else {
            indexLeft = indexMid;
            indexMid = (indexRight - indexLeft) /    INDEX_LENGTH / 2 * INDEX_LENGTH + indexLeft;
        }
    } while (indexLeft < indexMid);            // 注意是根据mid来进行判断

    // 只要符合范围就可以,不需要完全相等
    return indexMid;
}

// ============================================================================
// ==============================================================================
unsigned long CIpFinder::SearchIp(const char *pszIp,
                                  unsigned long indexStart,
                                  unsigned long indexEnd) const
{
    if (!this->IsRightIpString(pszIp)) {
        return 0;
    }
    return this->SearchIp(this->IpString2IpValue(pszIp), indexStart,
                          indexEnd);
}

// ==========================================================================================================
//    从指定位置获取一个十六进制的数 (读取3个字节, 主要用于获取偏移量, 与效率紧密相关的函数,尽可能优化)
// ==========================================================================================================
unsigned long CIpFinder::GetValue3(unsigned long indexStart) const
{
    if (!m_fpIpDataFile) {
        return 0;
    }

    //~~~~~~~~~~~~~~~~~~~~
    int nVal[3];
    unsigned long ulValue = 0;
    //~~~~~~~~~~~~~~~~~~~~

    fseek(m_fpIpDataFile, indexStart, SEEK_SET);
    for (int i = 0; i < 3; i++) {

        // 过滤高位,一次读取一个字符
        nVal[i] = fgetc(m_fpIpDataFile) & 0x000000FF;
    }

    for (int j = 2; j >= 0; --j) {

        // 因为读取多个16进制字符,叠加
        ulValue = ulValue * 0x100 + nVal[j];
    }
    return ulValue;
}

// ==========================================================================================================
//    从指定位置获取一个十六进制的数 (读取4个字节, 主要用于获取IP值, 与效率紧密相关的函数,尽可能优化)
// ==========================================================================================================
unsigned long CIpFinder::GetValue4(unsigned long indexStart) const
{
    if (!m_fpIpDataFile) {
        return 0;
    }

    //~~~~~~~~~~~~~~~~~~~~
    int nVal[4];
    unsigned long ulValue = 0;
    //~~~~~~~~~~~~~~~~~~~~

    fseek(m_fpIpDataFile, indexStart, SEEK_SET);
    for (int i = 0; i < 4; i++) {

        // 过滤高位,一次读取一个字符
        nVal[i] = fgetc(m_fpIpDataFile) & 0x000000FF;
    }

    for (int j = 3; j >= 0; --j) {

        // 因为读取多个16进制字符,叠加
        ulValue = ulValue * 0x100 + nVal[j];
    }
    return ulValue;
}

// ============================================================================
//    从指定位置获取字符串
// ============================================================================
unsigned long CIpFinder::GetString(std::string &str, unsigned long indexStart) const
{
    if (!m_fpIpDataFile) {
        return 0;
    }

    str.erase(0, str.size());

    fseek(m_fpIpDataFile, indexStart, SEEK_SET);
    //~~~~~~~~~~~~~~~~~~~~~~
    int nChar = fgetc(m_fpIpDataFile);
    unsigned long ulCount = 1;
    //~~~~~~~~~~~~~~~~~~~~~~

    

    // 读取字符串,直到遇到0x00为止
     while (nChar != 0x00) {

        // 依次放入用来存储的字符串空间中
        str += static_cast<char>(nChar);
        ++ulCount;
        nChar = fgetc(m_fpIpDataFile);
    }

    // 返回字符串长度
    return ulCount;
}

// ============================================================================
//    通过指定的偏移量来获取ip记录中的国家名和地区名,偏移量可由索引获取
//    ulOffset为Ip记录偏移量
// ============================================================================
void CIpFinder::GetAddressByOffset(unsigned long ulOffset,
                                   std::string &strCountry,
                                   std::string &strLocation) const
{
    if (!m_fpIpDataFile) {
        return;
    }

    // 略去4字节Ip地址
    ulOffset += IP_LENGTH;
    fseek(m_fpIpDataFile, ulOffset, SEEK_SET);

    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    // 读取首地址的值
    int nVal = (fgetc(m_fpIpDataFile) & 0x000000FF);
    unsigned long ulCountryOffset = 0;    // 真实国家名偏移
    unsigned long ulLocationOffset = 0; // 真实地区名偏移
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    // 为了节省空间,相同字符串使用重定向,而不是多份拷贝
    if (REDIRECT_MODE_1 == nVal) {

        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        // 重定向1类型
        unsigned long ulRedirect = this->GetValue3(ulOffset + 1); // 重定向偏移
                                                                    ///
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

        fseek(m_fpIpDataFile, ulRedirect, SEEK_SET);

        if ((fgetc(m_fpIpDataFile) & 0x000000FF) == REDIRECT_MODE_2) {

            // 混合类型1,重定向1类型进入后遇到重定向2类型 
            // 0x01 1字节
            // 偏移量 3字节 -----> 0x02 1字节 
            //                     偏移量 3字节 -----> 国家名
            //                     地区名
            ulCountryOffset = this->GetValue3(ulRedirect + 1);
            this->GetString(strCountry, ulCountryOffset);
            ulLocationOffset = ulRedirect + 4;
        } else {

            // 单纯的重定向模式1
            // 0x01 1字节
            // 偏移量 3字节 ------> 国家名
            //                      地区名
            ulCountryOffset = ulRedirect;
            ulLocationOffset = ulRedirect + this->GetString(strCountry,
                                                            ulCountryOffset);
        }
    } else if (REDIRECT_MODE_2 == nVal) {

        // 重定向2类型
        // 0x02 1字节
        // 国家偏移 3字节 -----> 国家名
        // 地区名
        ulCountryOffset = this->GetValue3(ulOffset + 1);
        this->GetString(strCountry, ulCountryOffset);

        ulLocationOffset = ulOffset + 4;
    } else {

        // 最简单的情况 没有重定向
        // 国家名
        // 地区名
        ulCountryOffset = ulOffset;
        ulLocationOffset = ulCountryOffset + this->GetString(strCountry,
                                                             ulCountryOffset);
    }

    // 读取地区
    fseek(m_fpIpDataFile, ulLocationOffset, SEEK_SET);
    if ((fgetc(m_fpIpDataFile) & 0x000000FF) == REDIRECT_MODE_2
    || (fgetc(m_fpIpDataFile) & 0x000000FF) == REDIRECT_MODE_1) {

        // 混合类型2(最复杂的情形,地区也重定向)
        // 0x01 1字节
        // 偏移量 3字节 ------> 0x02 1字节
        //                      偏移量 3字节 -----> 国家名
        //                      0x01 or 0x02 1字节
        //                      偏移量 3字节 ----> 地区名 偏移量为0表示未知地区
        ulLocationOffset = this->GetValue3(ulLocationOffset + 1);
    }

    this->GetString(strLocation, ulLocationOffset);
}

// ============================================================================
//    根据十六进制ip获取国家名地区名
// ============================================================================
void CIpFinder::GetAddressByIp(unsigned long ipValue,
                               std::string &strCountry,
                               std::string &strLocation) const
{
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    unsigned long ulIndexOffset = this->SearchIp(ipValue);
    unsigned long ulRecordOffset = this->GetValue3(ulIndexOffset + IP_LENGTH);
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    this->GetAddressByOffset(ulRecordOffset, strCountry, strLocation);
}

// ============================================================================
//    根据ip字符串获取国家名地区名
// ============================================================================
void CIpFinder::GetAddressByIp(const char *pszIp,
                               std::string &strCountry,
                               std::string &strLocation) const
{
    if (!this->IsRightIpString(pszIp)) {
        return;
    }
    this->GetAddressByIp(this->IpString2IpValue(pszIp), strCountry, strLocation);
}

// ============================================================================
//    将ip数据导出,start和end界定导出范围, 可通过SearchIp来获取
// ============================================================================
unsigned long CIpFinder::OutputData(const char *pszFileName,
                                    unsigned long indexStart,
                                    unsigned long indexEnd) const
{
    if (!m_fpIpDataFile || !pszFileName) {
        return 0;
    }

    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    FILE *fpOut = fopen(pszFileName, "wb");
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    if (!fpOut) {
        return 0;
    }

    if (0 == indexStart) {
        indexStart = m_indexStart;
    }

    if (0 == indexEnd) {
        indexEnd = m_indexEnd;
    }

    //~~~~~~~~~~~~~~~~~~~~~~~~
    char szEndIp[255];
    char szStartIp[255];
    std::string strCountry;
    std::string strLocation;
    unsigned long ulCount = 0;
    unsigned long ipValueEnd = 0;
    unsigned long ipValueStart = 0;
    //~~~~~~~~~~~~~~~~~~~~~~~~

    for (unsigned long i = indexStart; i < indexEnd; i += INDEX_LENGTH) {

        // 获取IP段的起始IP和结束IP, 起始IP为索引部分的前4位16进制
        // 结束IP在IP信息部分的前4位16进制中,靠索引部分指定的偏移量找寻
        ipValueStart = this->GetValue4(i);
        ipValueEnd = this->GetValue4(this->GetValue3(i + IP_LENGTH));

        // 导出IP信息,格式是 起始IP/t结束IP/t国家位置/t地域位置/n
        this->IpValue2IpString(ipValueStart, szStartIp, sizeof(szStartIp));
        this->IpValue2IpString(ipValueEnd, szEndIp, sizeof(szEndIp));
        this->GetAddressByOffset(this->GetValue3(i + IP_LENGTH), strCountry,
                                 strLocation);
        fprintf(fpOut, "%s/t%s/t%s/t%s/n", szStartIp, szEndIp,
                strCountry.c_str(), strLocation.c_str());
        ulCount++;
    }

    fclose(fpOut);

    // 返回导出总条数
    return ulCount;
}

// ============================================================================
//    通过ip值界定导出范围
// ==============================================================================
unsigned long CIpFinder::OutputDataByIp(const char *pszFileName,
                                        unsigned long ipValueStart,
                                        unsigned long ipValueEnd) const
{
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    unsigned long indexStart = this->SearchIp(ipValueStart);
    unsigned long indexEnd = this->SearchIp(ipValueEnd);
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    return this->OutputData(pszFileName, indexStart, indexEnd);
}

// ============================================================================
//    通过ip字符串界定导出范围
// ==============================================================================
unsigned long CIpFinder::OutputDataByIp(const char *pszFileName,
                                        const char *pszStartIp,
                                        const char *pszEndIp) const
{
    if (!this->IsRightIpString(pszStartIp) || !this->IsRightIpString(pszEndIp)) {
        return 0;
    }
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    unsigned long ipValueEnd = this->IpString2IpValue(pszEndIp);
    unsigned long ipValueStart = this->IpString2IpValue(pszStartIp);
    unsigned long indexEnd = this->SearchIp(ipValueEnd);
    unsigned long indexStart = this->SearchIp(ipValueStart);
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    return this->OutputData(pszFileName, indexStart, indexEnd);
}

// ============================================================================
//    判断给定IP字符串是否是合法的ip地址
// ==============================================================================
bool CIpFinder::IsRightIpString(const char* pszIp) const
{
    if (!pszIp) {
        return false;
    }

    int nLen = strlen(pszIp);
    if (nLen < 7) {

        // 至少包含7个字符"0.0.0.0"
        return false;
    }

    for (int i = 0; i < nLen; ++i) {
        if (!isdigit(pszIp[i]) && pszIp[i] != '.') {
            return false;
        }

        if (pszIp[i] == '.') {
            if (0 == i) {
                if (!isdigit(pszIp[i + 1])) {
                    return false;
                }
            } else if (nLen - 1 == i) {
                if (!isdigit(pszIp[i - 1])) {
                    return false;
                }
            } else {
                if (!isdigit(pszIp[i - 1]) || !isdigit(pszIp[i + 1])) {
                    return false;
                }
            }
        }
    }
    return true;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值