免费纯真IP地址数据库的解析

ip地址数据库,在现在互联网时代非常有用,比如大型网站的用户安全保护系统,就常常会根据ip反查的信息,甄别账号的一些不安全登录行为,比如跨区域登录问题等。ip其实关联了一些有信息,比如区域,所在运营商,一些收录全的,甚至包括具体经纬度,像百度的IP定位api就比较全。下面来介绍一下“ 纯真IP地址数据库qqwry”的格式以及解析

以下是“ 纯真IP地址数据库qqwry”官网对其的介绍。


纯真版IP地址数据库是当前网络上最权威、地址最精确、IP记录以及网吧数据最多的IP地址数据库。收集了包括中国电信、中国移动、中国联通、铁通、长城宽带等各 ISP 的最新准确 IP 地址数据。通过大家的共同努力打造一个没有未知数据,没有错误数据的QQ IP。IP数据库每5天更新一次,请大家定期更新最新的IP数据库!


格式

+———-+

| 文件头 | (8字节)

+———-+

| 记录区 | (不定长)

+———-+

| 索引区 | (大小由文件头决定)

+———-+

使用java语言解析的两种思路:


使用内存映射文件方式读取,使用java的MappedByteBuffer 将原数据文件映射到MappedByteBuffer对象中,然后通过MappedByteBuffer 提供的字节读取方式实现ip的查找。搜索是在索引区使用二分法

使用byte数组读取,及将二进制的数据库信息全都按顺序读入到一个数组中,由于数据是有格式的,我们便可计算根据索引区和记录区在数组中的位置,当查询ip时,从数组中的索引区开始通过二分查找方式找到IP地址对应的国家和区域的位置,然后从数组中取出地区信息。


热升级思路:

使用一个可调度的单线程的线程池,线程定时检测qqwry.dat文件是否修改,若修改则重新将数据重新载入,载入过程可使用可重入锁ReentrantLock来锁住资源,避免在更新的过程中脏查询

两种解析方式的实现源码如下:

方式一(MappedByteBuffer ):

package com.difeng.qqwry1;

import java.io.File;

import java.io.IOException;

import java.io.RandomAccessFile;

import java.nio.ByteOrder;

import java.nio.MappedByteBuffer;

import java.nio.channels.FileChannel;

import java.util.concurrent.Executors;

import java.util.concurrent.TimeUnit;

import java.util.concurrent.locks.ReentrantLock;

/**

 * @Description:ip定位查找工具(使用内存映射文件方式读取,线程安全)

 * @author:difeng

 * @date:2016年12月11日

 */

public class IPLocation {


    private static final int IP_RECORD_LENGTH = 7;


    private static final byte REDIRECT_MODE_1 = 0x01;


    private static final byte REDIRECT_MODE_2 = 0x02;


    private MappedByteBuffer mbbFile;


    private static Long lastModifyTime = 0L;


    public static boolean enableFileWatch = false;


    private static ReentrantLock lock = new ReentrantLock();


    private File qqwryFile;


    private long firstIndexOffset;


    private long lastIndexOffset;


    private long totalIndexCount;


    public IPLocation(String filePath) throws Exception {

        this.qqwryFile = new File(filePath);

        load();

        if (enableFileWatch) {

            watch();

        }

    }


    private void watch(){

        Executors.newScheduledThreadPool(1).scheduleAtFixedRate(new Runnable() {

            @Override

            public void run() {

                long time = qqwryFile.lastModified();

                if (time > lastModifyTime) {

                    lastModifyTime = time;

                    try {

                        load();

                    } catch (Exception e) {

                        e.printStackTrace();

                    }

                }

            }

        }, 1000L, 30000L, TimeUnit.MILLISECONDS);

    }


    public long read4ByteAsLong(long pos) {

        mbbFile.position((int)pos);

        return 0xFFFFFFFFL & mbbFile.getInt();

    }


    public long read3ByteAsLong(long pos){

        mbbFile.position((int)pos);

        return 0xFFFFFFL & mbbFile.getInt();

    }



    @SuppressWarnings("resource")

    private void load() throws Exception {

        lastModifyTime = qqwryFile.lastModified();

        lock.lock();

        try {

            mbbFile = new RandomAccessFile(qqwryFile, "r")

                    .getChannel()

                    .map(FileChannel.MapMode.READ_ONLY, 0, qqwryFile.length());

            mbbFile.order(ByteOrder.LITTLE_ENDIAN);

            firstIndexOffset = read4ByteAsLong(0);

            lastIndexOffset = read4ByteAsLong(4);

            totalIndexCount = (lastIndexOffset - firstIndexOffset) / IP_RECORD_LENGTH + 1;

        } finally {

            lock.unlock();

        }

    }


    /**

     * @Description:将“.”号分隔的字符串转换为long类型的数字,字节序例如:

     * ip:182.92.240.48 16进制表示(B6.5C.F0.30)

     * 转换后ip的16进制表示:0xB65CF030

     * @param ipStr

     * @return:long

     */

    private static long inet_pton(String ipStr) {

        if(ipStr == null){

            throw new NullPointerException("ip不能为空");

        }

        String [] arr = ipStr.split("\\.");

        long ip = (Long.parseLong(arr[0]) & 0xFFL) << 24 & 0xFF000000L;

        ip |= (Long.parseLong(arr[1]) & 0xFFL) << 16 & 0xFF0000L;

        ip |= (Long.parseLong(arr[2]) & 0xFFL) << 8 & 0xFF00L;

        ip |= (Long.parseLong(arr[3]) & 0xFFL);

        return ip;

    }


    private long search(long ip) {

        long low = 0;

        long high = totalIndexCount;

        long mid = 0;

        while(low <= high) {

            mid = (low + high) >>> 1 ;

            long indexIP = read4ByteAsLong(firstIndexOffset + (mid - 1) * IP_RECORD_LENGTH);

            long nextIndexIP = read4ByteAsLong(firstIndexOffset + mid * IP_RECORD_LENGTH);

            if(indexIP <= ip && ip < nextIndexIP) {

                return read3ByteAsLong(firstIndexOffset + (mid - 1) * IP_RECORD_LENGTH + 4);

            } else {

                if(ip > indexIP) {

                    low = mid + 1;

                } else if(ip < indexIP) {

                    high = mid - 1;

                }

            }

        }

        return -1;

    }


    private Location readIPLocation(long offset) {

        try {

            mbbFile.position((int)offset + 4);

            Location loc = new Location();

            byte redirectMode = mbbFile.get();

            if (redirectMode == REDIRECT_MODE_1) {

                long countryOffset = read3ByteAsLong((int)offset + 5);

                mbbFile.position((int)countryOffset);

                redirectMode = mbbFile.get();

                if (redirectMode == REDIRECT_MODE_2) {

                    loc.country = readString(read3ByteAsLong(countryOffset + 1));

                    mbbFile.position((int)countryOffset + 4);

                } else {

                    loc.country = readString(countryOffset);

                }

                loc.area = readArea(mbbFile.position());

            } else if (redirectMode == REDIRECT_MODE_2) {

                loc.country = readString(read3ByteAsLong((int)offset + 5));

                loc.area = readArea((int)offset + 8);

            } else {

                loc.country = readString(mbbFile.position() - 1);

                loc.area = readArea(mbbFile.position());

            }

            return loc;

        } catch (Exception e) {

            return null;

        }

    }


    private String readArea(int offset) {

        mbbFile.position(offset);

        byte redirectMode = mbbFile.get();

        if (redirectMode == REDIRECT_MODE_1 || redirectMode == REDIRECT_MODE_2) {

            long areaOffset = read3ByteAsLong((int)offset + 1);

            if (areaOffset == 0){

                return "";

            } else {

                return readString(areaOffset);

            }

        } else {

            return readString(offset);

        }

    }


    private String readString(long offset) {

        try {

            mbbFile.position((int)offset);

            byte[] buf = new byte[128];

            int i;

            for (i = 0, buf[i] = mbbFile.get(); buf[i] != 0; buf[++i] = mbbFile.get());


            if (i != 0){

                return new String(buf, 0, i, "GBK");

            }

        } catch (IOException e) {

            e.printStackTrace();

        }

        return "";

    }


    public Location fetchIPLocation(String ip) {

        lock.lock();

        try {

            long offset = search(inet_pton(ip));

            if(offset != -1){

                return readIPLocation(offset);

            }

        } finally {

            lock.unlock();

        }

        return null;

    }

}



方式二(数组方式):

package com.difeng.qqwry2;

import java.io.ByteArrayOutputStream;

import java.io.File;

import java.io.FileInputStream;

import java.io.IOException;

import java.io.UnsupportedEncodingException;

import java.util.concurrent.Executors;

import java.util.concurrent.TimeUnit;

import java.util.concurrent.locks.ReentrantLock;

/**

 * @Description:ip定位(使用byte数据方式读取)

 * @author:difeng

 * @date:2016年12月13日

 */

public class IPLocation {


    private byte[] data;


    private long firstIndexOffset;


    private long lastIndexOffset;


    private long totalIndexCount;


    private static final byte REDIRECT_MODE_1 = 0x01;


    private static final byte REDIRECT_MODE_2 = 0x02;


    static final long IP_RECORD_LENGTH = 7;


    private static ReentrantLock lock = new ReentrantLock();


    private static Long lastModifyTime = 0L;


    public static boolean enableFileWatch = false;


    private File qqwryFile;


    public IPLocation(String filePath) throws Exception {

        this.qqwryFile = new File(filePath);

        load();

        if(enableFileWatch){

            watch();

        }

    }


    private void watch() {

        Executors.newScheduledThreadPool(1).scheduleAtFixedRate(new Runnable() {

            @Override

            public void run() {

                long time = qqwryFile.lastModified();

                if (time > lastModifyTime) {

                    lastModifyTime = time;

                    try {

                        load();

                        System.out.println("reload");

                    } catch (Exception e) {

                        e.printStackTrace();

                    }

                }

            }

        }, 1000L, 5000L, TimeUnit.MILLISECONDS);

    }


    private void load() throws Exception {

        lastModifyTime = qqwryFile.lastModified();

        ByteArrayOutputStream out = null;

        FileInputStream in = null;

        lock.lock();

        try {

            out = new ByteArrayOutputStream();

            byte[] b = new byte[1024];

            in = new FileInputStream(qqwryFile);

            while(in.read(b) != -1){

                out.write(b);

            }

            data = out.toByteArray();

            firstIndexOffset = read4ByteAsLong(0);

            lastIndexOffset = read4ByteAsLong(4);

            totalIndexCount = (lastIndexOffset - firstIndexOffset) / IP_RECORD_LENGTH + 1;

            in.close();

            out.close();

        } finally {

            try {

                if(out != null) {

                    out.close();

                }

                if(in != null) {

                    in.close();

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,544评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,430评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,764评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,193评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,216评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,182评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,063评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,917评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,329评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,543评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,722评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,425评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,019评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,671评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,825评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,729评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,614评论 2 353

推荐阅读更多精彩内容