当前位置:编程学习 > JAVA >>

纯真IP库的结构分析及一个查询类

 

  个人网站上有个功能,记录访问者的IP及其归属地。最初我偷懒通过一个WebService来查询IP归属地,后来觉得通过这种方法响应时间长,资源耗费大,而且对那个WebSerice的依赖度太高,如果它挂了或者网络原因,经常要到超时才返回。所以,我打算直接从本地的纯真IP库里查询。

    纯真库的数据结构在http://lumaqq.linuxsir.org/article/qqwry_format_detail.html上讲的很详细了。简单地讲数据文件分成三个区域:

    1、文件头(8个字节,前4字节是指向索引区第一条记录,后4字节指向索引区最后一条记录)

    2、记录区(一个记录包含IP地址,国家记录,地区记录,后两个记录有可能是字符串,也可能是重定向,有多种重定向模式)

    3、索引区(一个索引定长7个字节,前4字节是IP地址(little-endian),后3字节指向对应记录区的位置,这里的位置指从文件头开始计算的偏移字节)

   虽然这个库结构工作的很好,效率也没有问题,但是我觉得设计的有点小复杂了。而且,如果记录区中有条记录A,是重定向到记录B中的,假如我删除了记录B,查询记录A的时候就会有问题。当然,可以在删除记录B的时候进行相应处理,只是有些麻烦。如果把文件结构改成如下,应该处理起来会更方便一些:

   1、文件头(与原库一样)

   2、字符串区

   3、索引区(4字节的IP地址,4字节的偏移值,4字节的偏移值)

所有字符串放在字符串区中,统一管理。索引区中放IP地址,国家记录的“指针”和区域记录的“指针”,所谓的“指针”是对应到字符串区中某条的字符串偏移值。

 

不过既然纯真IP库是这么设计的,我只好根据它的结构来进行相应的查询。

索引区的记录是从小到大排列的,可以用二分法来查询。

IP库中索引的IP地址,并不是连续的,举个例子,192.168.0.0的后一条记录并不是192.168.0.1,可能是192.169.0.0,也就是说,它存储的一个是IP段。所以要做一个类似于“四舍五入”的处理。好在大部分情况下,我们都只要舍掉就可以了,比如查询192.168.1.1应该匹配192.168.0.0而不是192.169.0.0。

 

import java.io.*;

 

public class IPSeeker

{

    protected RandomAccessFile ipDataFile;

    protected final int RECORD_LEN = 7;

    protected final int MODE_1 = 0x01; //重定向国家记录,地区记录

    protected final int MODE_2 = 0x02; //重定向国家记录,有地区记录

    protected final int MODE_3 = 0x03; //default

    protected long indexBegin;

    protected long indexEnd;

        

    public IPSeeker() throws Exception

    {

        //打开纯真IP库数据文件

        ipDataFile = new RandomAccessFile("qqwry.dat", "r");

        

        indexBegin = readLong(4, 0);            

        indexEnd = readLong(4, 4);

    }

    

    public static void main(String[] args) throws Exception

    {

        IPSeeker seeker = new IPSeeker();//may throw Exception

        String result = seeker.search("111.2.13.4");//输入查询的IP地址

        System.out.println(result);

        seeker.close();//关闭,若不调用close,将在finalize关闭

        seeker = null;

    }

    

    

    @Override

    protected void finalize() throws Throwable

    {

        try

        {

            ipDataFile.close();

        } 

        catch (IOException e)

        {

            

        }

        super.finalize();

    }

    

    

    public void close()

    {

        try

        {

            ipDataFile.close();

        } 

        catch (IOException e)

        {

            

        }

    }

 

    public String search(String ipStr) throws Exception

    {

        //采用二分法查询

        long recordCount = (indexEnd - indexBegin) / 7 + 1;

        

        long itemStart = 0;

        long itemEnd = recordCount - 1;

        long ip = IPSeeker.stringIP2Long(ipStr);

        long middle = 0;

        long midIP = 0;

        while(itemStart <= itemEnd)

        {

            middle = (itemStart +  itemEnd) / 2;

            midIP = readLong(4, indexBegin + middle * 7);

            //String temp = IPSeeker.long2StringIP(midIP);

            if(midIP == ip)

            {

                break;

            }

            else if(midIP < ip)

            {

                itemStart = middle + 1;

            }

            else//midIP > ip

            {

                itemEnd = middle - 1;

            }

        }

        

        //若无完全匹配结果,则向前匹配

       

补充:软件开发 , Java ,
CopyRight © 2012 站长网 编程知识问答 www.zzzyk.com All Rights Reserved
部份技术文章来自网络,