利用纯真数据库获取ip归属地

在项目中经常会遇到获取IP地址归属地的需求。下面介绍一下如何纯真社区版IP库(qqwry.dat)获取ip归属地的工具类。

纯真IP库是一个用于检测中国互联网IP地址归属地的工具库,它提供了一种便捷的方式来识别用户的地理位置信息,通常用于网站、应用的防刷、地区定向等功能。这个库基于电信运营商的数据,将IP地址映射到具体的省份、城市甚至更详细的区县级别。使用纯真IP库可以帮助开发者快速验证用户IP,并基于位置信息做出相应的服务策略。

public class IPUtils {
    private static final Logger log = LoggerFactory.getLogger(IPUtils.class);

    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;

    private static ReentrantLock lock = new ReentrantLock();
    private static ReentrantLock lockArgs = new ReentrantLock();
    private File qqwryFile;

    private long firstIndexOffset;

    private long lastIndexOffset;

    private long totalIndexCount;

    private static String DATA_PATH = "qqwry.dat";

    // 根据IP地址获取ip归属地
    public String getCountry(String ip) {
        lockArgs.lock();
        String lo = "";
        try {
            if(null == qqwryFile || null == mbbFile){
                init();
            }
            mbbFile.position(0);
            firstIndexOffset = read4ByteAsLong(0);
            lastIndexOffset = read4ByteAsLong(4);
            totalIndexCount = (lastIndexOffset - firstIndexOffset) / IP_RECORD_LENGTH + 1;
            lo = readIPLocation(search(inet_pton(ip))).getCountry();
        }finally {
            lockArgs.unlock();
        }
        return lo;
    }

    public void init(){
        this.qqwryFile = new File(DATA_PATH);
        try {
            load();
        } catch (Exception e) {
            log.error("加载资源文件异常,"+e.getMessage());
        }
        
    }

    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();
    }


    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);
        } finally {
            lock.unlock();
        }
    }
    public 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;
    }

    public 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;
    }

    public 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.setCountry(readString(read3ByteAsLong(countryOffset + 1)));
                    mbbFile.position((int) countryOffset + 4);
                } else {
                    loc.setCountry( readString(countryOffset));
                }
                loc.setArea(readArea(mbbFile.position()));
            } else if (redirectMode == REDIRECT_MODE_2) {
                loc.setCountry (readString(read3ByteAsLong((int) offset + 5)));
                loc.setArea  (readArea((int) offset + 8));
            } else {
                loc.setCountry  (readString(mbbFile.position() - 1));
                loc.setArea  (readArea(mbbFile.position()));
            }
            return loc;
        } catch (Exception e) {
            return null;
        }
    }

    public String readArea(long offset) {
        mbbFile.position((int) 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 "";
    }
}

  • 18
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值