说明:IP地址库来自QQwry.dat数据库文件,通过解析地址库当中的ip,已经细化最后获取的信息:获取ip地址对应的:国家 / 省 / 市 / 运营商ISP信息。

解析主要用到三个类:

(1) IPSeeker.java:通过IP地址查询解析相关信息

(2) SplitAddress.java:IPSeeker解析出的完整信息做进一步切分,划分为0级地址(国家/省)、1级地址(市级)、2级地址(区)、运营商ISP信息。

(3) IPEntity.java:定义的实体字段,包括nation、province、city、region.

-----------------------------------------------------------------------------------------------------------------------------

(1) IPSeeker.java

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.io.UnsupportedEncodingException;
import java.nio.ByteOrder;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;  public class IPSeeker{private Log log = LogFactory.getLog(IPSeeker.class);//:::::::::::: Class IPLocation ::::::::::::::::::::://public class IPLocation{//:::::::::::: Fields :::::::::::::://private String country; //完全地址:省/市/区private String isp;     //使用的网络(运营商ISP)//:::::::::::: Province ::::::::::::://public String getCountry() {return country;}public void setCountry(String country) {this.country = country;}public String getIsp(){if("CZ88.NET".equals(isp)){isp = ""; }return isp;}        public void setIsp(String isp){this.isp = isp;}//::::::::: Class Constructor ::::::::::::://public IPLocation(){isp = "";country = "";}public IPLocation getCopy(){IPLocation ret = new IPLocation();ret.country = country;ret.isp = isp;return ret;}}//:::::::::::: Class IPEntry ::::::::::::::::::::://public class IPEntry{public String beginIp;public String endIp;public String country;public String isp;public IPEntry(){beginIp = endIp = country = isp = "";}@Overridepublic String toString(){return this.isp+" "+this.country+"IP范围:"+this.beginIp+"-"+this.endIp;}}//一些固定常量,比如记录长度等private static final int IP_RECORD_LENGTH = 7;private static final byte isp_FOLLOWED = 0x01;private static final byte NO_isp = 0x2;// 用来做为cache,查询一个ip时首先查看cache,以减少不必要的重复查找private MappedByteBuffer mbb;// 随机文件访问类private RandomAccessFile ipFile;// 内存映射文件private HashMap<String, IPLocation> ipCache;// 起始地区的开始和结束的绝对偏移private int ipBegin, ipEnd;public IPSeeker(File ipFile) throws Exception{this.ipFile = new RandomAccessFile(ipFile, "r");ipCache = new HashMap<String, IPLocation>();FileChannel fc = this.ipFile.getChannel();mbb = fc.map(FileChannel.MapMode.READ_ONLY, 0, ipFile.length());mbb.order(ByteOrder.LITTLE_ENDIAN);ipBegin = readInt(0);ipEnd = readInt(4);if(ipBegin==-1 || ipEnd==-1){throw new IOException("IP地址信息文件格式有错误,IP显示功能将无法使用");}log.debug("使用IP地址库:"+ipFile.getAbsolutePath());}/*** 给定一个ip 得到一个 ip地址信息* @param ip* @return*/public String getAddress(String ip){return getCountry(ip) + " " + getIsp(ip);}/*** 根据IP得到国家名* @param ip IP的字符串形式* @return 国家名字符串*/public String getCountry(String ip){IPLocation cache = getIpLocation(ip);return cache.getCountry();}/*** 根据IP得到地区名* @param ip  IP的字符串形式* @return  地区名字符串*/public String getIsp(String ip){IPLocation cache = getIpLocation(ip);return cache.getIsp();}public IPLocation getIpLocation(String ip){IPLocation ipLocation = null;try {if(ipCache.get(ip)!=null){return ipCache.get(ip);}ipLocation = getIPLocation(getIpByteArrayFromString(ip));if(ipLocation!=null){ipCache.put(ip, ipLocation);}} catch (Exception e) {log.error(e);}if(ipLocation==null){ipLocation = new IPLocation();ipLocation.setCountry("未知国家");ipLocation.setIsp("未知地区");}return ipLocation;}/***  给定一个地点的不完全名字,得到一系列包含s子串的IP范围记录* @param s 地点子串* @return 包含IPEntry类型的List*/public List<IPEntry> getIPEngries(String s){List<IPEntry> ret = new ArrayList<IPEntry>();byte[] b4=new byte[4];int endOffset = ipEnd + 4;for(int offset=ipBegin+4; offset<=endOffset; offset+=IP_RECORD_LENGTH){//读取结束IP偏移int temp = readInt3(offset);// 如果temp不等于-1,读取IP的地点信息if(temp!=-1){IPLocation loc = getIPLocation(temp);// 判断是否这个地点里面包含了s子串,如果包含了,添加这个记录到List中,如果没有,继续if (loc.country.indexOf(s) != -1 || loc.isp.indexOf(s) != -1) {IPEntry entry = new IPEntry();entry.country = loc.country;entry.isp = loc.isp;// 得到起始IPreadIP(offset - 4, b4);entry.beginIp = getIpStringFromBytes(b4);//  得到结束IPreadIP(temp, b4);entry.endIp = getIpStringFromBytes(b4);// 添加该记录ret.add(entry);}}}return ret;}/*** 根据ip搜索ip信息文件,得到IPLocation结构,所搜索的ip参数从类成员ip中得到* @param ip 要查询的IP* @return  IPLocation结构*/private IPLocation getIPLocation(byte[] ip){IPLocation info = null;int offset = locateIP(ip);if (offset != -1) {info = getIPLocation(offset);}return info;}//---------以下为内部方法-------------///*** 读取4个字节* @param offset* @return*/private int readInt(int offset) {mbb.position(offset);return mbb.getInt();}private int readInt3(int offset) {mbb.position(offset);return mbb.getInt() & 0x00FFFFFF;}/*** 从内存映射文件的offset位置得到一个0结尾字符串* @param offset* @return*/private String readString(int offset) {try {byte[] buf=new byte[100];mbb.position(offset);int i;for (i = 0, buf[i] = mbb.get(); buf[i] != 0; buf[++i] = mbb.get()) {}if (i != 0) {return getString(buf, 0, i, "GBK");}}catch (IllegalArgumentException e){log.error(e);}return "";}/*** 从offset位置读取四个字节的ip地址放入ip数组中,读取后的ip为big-endian格式,但是* 文件中是little-endian形式,将会进行转换* @param offset* @param ip*/private void readIP(int offset, byte[] ip) {mbb.position(offset);mbb.get(ip);byte temp = ip[0];ip[0] = ip[3];ip[3] = temp;temp = ip[1];ip[1] = ip[2];ip[2] = temp;}/*** 把类成员ip和beginIp比较,注意这个beginIp是big-endian的* @param ip 要查询的IP* @param beginIp  和被查询IP相比较的IP* @return 相等返回0,ip大于beginIp则返回1,小于返回-1。*/private int compareIP(byte[] ip, byte[] beginIp) {for (int i = 0; i < 4; i++) {int r = compareByte(ip[i], beginIp[i]);if (r != 0){return r;}}return 0;}/*** 把两个byte当作无符号数进行比较* @param b1* @param b2* @return 若b1大于b2则返回1,相等返回0,小于返回-1*/private int compareByte(byte b1, byte b2) {if ((b1 & 0xFF) > (b2 & 0xFF)){ //比较是否大于return 1;}else if((b1^b2)==0){ //判断是否相等return 0;}else{return -1;}}/*** 这个方法将根据ip的内容,定位到包含这个ip国家地区的记录处,返回一个绝对偏移* 方法使用二分法查找。* @param ip 要查询的IP* @return 如果找到了,返回结束IP的偏移,如果没有找到,返回-1*/private int locateIP(byte[] ip) {int m = 0;int r;byte[] b4=new byte[4];// 比较第一个ip项readIP(ipBegin, b4);r = compareIP(ip, b4);if (r == 0) {return ipBegin;} else if (r < 0) {return -1;}//  开始二分搜索for (int i = ipBegin, j = ipEnd; i < j;) {m = getMiddleOffset(i, j);readIP(m, b4);r = compareIP(ip, b4);// log.debug(Utils.getIpStringFromBytes(b));if (r > 0) {i = m;} else if (r < 0) {if (m == j) {j -= IP_RECORD_LENGTH;m = j;} else {j = m;}} else {return readInt3(m + 4);}}// 如果循环结束了,那么i和j必定是相等的,这个记录为最可能的记录,但是并非// 肯定就是,还要检查一下,如果是,就返回结束地址区的绝对偏移m = readInt3(m + 4);readIP(m, b4);r = compareIP(ip, b4);if (r <= 0) {return m;} else {return -1;}}/*** 得到begin偏移和end偏移中间位置记录的偏移* @param begin* @param end* @return*/private int getMiddleOffset(int begin, int end) {int records = (end - begin) / IP_RECORD_LENGTH;records >>= 1;if (records == 0) {records = 1;}return begin + records * IP_RECORD_LENGTH;}/*** @param offset* @return*/private IPLocation getIPLocation(int offset) {IPLocation loc = new IPLocation();// 跳过4字节ipmbb.position(offset + 4);// 跳过4字节ipbyte b = mbb.get();if (b == isp_FOLLOWED) {// 读取国家偏移int countryOffset = readInt3();// 跳转至偏移处mbb.position(countryOffset);// 再检查一次标志字节,因为这个时候这个地方仍然可能是个重定向b = mbb.get();if (b == NO_isp) {loc.country = readString(readInt3());mbb.position(countryOffset + 4);} else {loc.country = readString(countryOffset);}// 读取地区标志loc.isp = readisp(mbb.position());} else if (b == NO_isp) {loc.country = readString(readInt3());loc.isp = readisp(offset + 8);} else {loc.country = readString(mbb.position() - 1);loc.isp = readisp(mbb.position());}return loc;}/*** @param offset* @return*/private String readisp(int offset) {mbb.position(offset);byte b = mbb.get();if (b == 0x01 || b == 0x02){int ispOffset = readInt3();if(ispOffset == 0){return "未知地区";}else{return readString(ispOffset);}}else{return readString(offset);}}/*** 从内存映射文件的当前位置开始的3个字节读取一个int* @return*/private int readInt3(){return mbb.getInt() & 0x00FFFFFF;}/***  从ip的字符串形式得到字节数组形式:字符串序列化为二进制文件* @param ip 字符串形式的ip* @return 字节数组形式的ip*/private static byte[] getIpByteArrayFromString(String ip) throws Exception{byte[] ret = new byte[4];java.util.StringTokenizer st = new java.util.StringTokenizer(ip, ".");try{ret[0] = (byte)(Integer.parseInt(st.nextToken()) & 0xFF);ret[1] = (byte)(Integer.parseInt(st.nextToken()) & 0xFF);ret[2] = (byte)(Integer.parseInt(st.nextToken()) & 0xFF);ret[3] = (byte)(Integer.parseInt(st.nextToken()) & 0xFF);}catch(Exception e){throw e;}return ret;}/*** 根据某种编码方式将字节数组转换成字符串* @param b 字节数组* @param offset 要转换的起始位置* @param len 要转换的长度* @param encoding  编码方式* @return 如果encoding不支持,返回一个缺省编码的字符串*/private static String getString(byte[] b, int offset, int len, String encoding) {try {return new String(b, offset, len, encoding);}catch(UnsupportedEncodingException e){return new String(b, offset, len);}}/*** @param ip ip的字节数组形式* @return 字符串形式的ip*/private static String getIpStringFromBytes(byte[] ip) {StringBuffer sb = new StringBuffer();sb.append(ip[0] & 0xFF);sb.append('.');sb.append(ip[1] & 0xFF);sb.append('.');sb.append(ip[2] & 0xFF);sb.append('.');sb.append(ip[3] & 0xFF);return sb.toString();}}

(2)SplitAddress.java

import java.io.File;/*** @Note 地址切分____将全域地址切分为:  0级地址:国家或省*                                    1级地址:市*                                      2级地址:区* *       调用时直接调用IPseeker类:eg.  ipseeker.getAddress("xxx.xxx.xxx.xxx");*                                    ipseeker.getCountry("xxx.xxx.xxx.xxx");*                                    ipseeker.getIsp("xxx.xxx.xxx.xxx");*       由于ipentity.nation,province,city,region是作的切分,故 ipentity.getNation()等没有输入参数,调用直接输出*                                  ipentity.getNation();*                                  ipentity.getProvince();*                                    ipentity.getCity();*                                    ipentity.getRegion();*       测试ip:202.112.110.0 -----北京市 北京邮电大学*              27.184.95.146 ------河北省石家庄市 电信*              219.136.134.157 -----广东省广州市越秀区 电信ADSL*              59.45.1.151 --------辽宁省盘锦市双台子区 胜星网吧(辽河北路与城北路交汇)*              61.128.101.255 -----新疆乌鲁木齐市 电信*              220.182.50.226 -----西藏拉萨市 波斯湾网络会所(宇拓路30号)*              210.136.134.157 -----日本  CZ88.NET*              207.46.13.93 -----美国  Microsoft公司*              27.122.12.0 ------香港 智通(Pacswitch)环球电讯*              */
public class SplitAddress {public static void main(String[] args){try {IPSeeker ipseeker = new IPSeeker(new File("e:/aleiye/data/QQWry.dat"));IPEntity ipentity = new IPEntity();String ipaddress = "27.122.12.0";SplitAddress splitaddress = new SplitAddress();splitaddress.SplitAddressAction(ipaddress, ipseeker, ipentity); //切分获得多级地址System.out.println("完整ip信息:"+ipseeker.getAddress(ipaddress));System.out.println("完全地址:省/市/区:"+ipseeker.getCountry(ipaddress));System.out.println("nation:"+ipentity.getNation());System.out.println("province:"+ipentity.getProvince());System.out.println("city:"+ipentity.getCity());System.out.println("region:"+ipentity.getRegion());System.out.println("使用的网络(运营商ISP):"+ipseeker.getIsp(ipaddress));} catch (Exception e) {e.printStackTrace();}}public void SplitAddressAction(String ipaddress, IPSeeker ipseeker, IPEntity ipentity){try {          String alladdress = ipseeker.getCountry(ipaddress);    String[] part;//全国省市里唯一没有"市"字样的只有这4个省,直接作逗号","切分if(alladdress.startsWith("新疆")){ipentity.setProvince("新疆");alladdress = alladdress.replace("新疆", "新疆,");}else if(alladdress.startsWith("西藏")){ipentity.setProvince("西藏");alladdress = alladdress.replace("西藏", "西藏,");}else if(alladdress.startsWith("内蒙古")){ipentity.setProvince("内蒙古");alladdress = alladdress.replace("内蒙古", "内蒙古,");}else if(alladdress.startsWith("宁夏")){ipentity.setProvince("宁夏");alladdress = alladdress.replace("宁夏", "宁夏,");}alladdress = alladdress.replaceAll("省", "省,").replaceAll("市", "市,"); //最多切成3段:辽宁省,盘锦市,双台子区;part = alladdress.split(",");if(part.length==1){//只有1级地址ipentity.setNation(part[0]);ipentity.setProvince(part[0]);}else if(part.length==2){//有2级地址ipentity.setProvince(part[0]);ipentity.setCity(part[1]);}else if(part.length==3){//有3级地址ipentity.setProvince(part[0]);ipentity.setCity(part[1]);ipentity.setRegion(part[2]);}} catch (Exception e) {e.printStackTrace();}}}

(3) IPEntity.java

public class IPEntity {String nation;   //国家:0级地址String province; //省:0级地址String city;     //市:1级地址String region;   //区:2级地址public String getNation() {return nation;}public void setNation(String nation) {this.nation = nation;}public String getProvince() {return province;}public void setProvince(String province) {this.province = province;}public String getCity() {return city;}public void setCity(String city) {this.city = city;}public String getRegion() {return region;}public void setRegion(String region) {this.region = region;}public IPEntity(){}}

转载于:https://www.cnblogs.com/DianaCody/p/5425677.html

IP地址库解析——读取IP地址获得实际地理位置信息的java源码实现相关推荐

  1. Ip探针_信息探针_社工查询个人信息php网站源码

    介绍: Ip探针_信息探针_社工查询个人信息php网站源码 他可以查IP 精确位置 然后社工地址的话更准 需要的东西: 服务器或主机一部 2.浏览器搜索 二级域名分发 很多免费的 3.绑定域名 4.上 ...

  2. 华为OD机试(21-40)老题库解析Java源码系列连载ing

    华为OD机试算法题新老题库练习及源码 老题库 21.字符串序列判定 22.最长的指定瑕疵度的元音子串 23.处理器问题 24.单向链表中间节点 25.字符串重新排列.字符串重新排序 26.完美走位 2 ...

  3. Tika结合Tesseract-OCR 实现光学汉字识别(简体、宋体的识别率百分之百)—附Java源码实现及真实测试数据和训练集下载地址...

     OCR(Optical character recognition) -- 光学文字识别,是图像处理的一个重要分支,中文的识别具有一定挑战性,特别是手写体和草书的识别,是重要和热门的科学研究方向.可 ...

  4. Tika结合Tesseract-OCR 实现光学汉字识别(简体、宋体的识别率百分之百)—附Java源码、测试数据和训练集下载地址...

     OCR(Optical character recognition) -- 光学字符识别,是图像处理的一个重要分支,中文的识别具有一定挑战性,特别是手写体和草书的识别,是重要和热门的科学研究方向.可 ...

  5. java毕业设计——基于java+JSP+sqlserver的Smart系统-题库及试卷管理模块设计与实现(毕业论文+程序源码)——学生信息管理系统模板2

    基于java+JSP+sqlserver的Smart系统-题库及试卷管理模块设计与实现(毕业论文+程序源码) 大家好,今天给大家介绍基于java+JSP+sqlserver的Smart系统-题库及试卷 ...

  6. 面试官系统精讲Java源码及大厂真题 - 02 String、Long 源码解析和面试题

    02 String.Long 源码解析和面试题 劳动是一切知识的源泉. --陶铸 引导语 String 和 Long 大家都很熟悉,本小节主要结合实际的工作场景,来一起看下 String 和 Long ...

  7. 解析java源文件_使用JDT.AST解析java源码

    在做java源码的静态代码审计时,最基础的就是对java文件进行解析,从而获取到此java文件的相关信息: 在java文件中所存在的东西很多,很复杂,难以用相关的正则表达式去一一匹配.但是,eclip ...

  8. java 读取apk_readApk 读取apk安装包的各种信息,帮助java后台开发者,并提供方便 Develop 238万源代码下载- www.pudn.com...

    文件名称: readApk下载 收藏√  [ 5  4  3  2  1 ] 开发工具: Java 文件大小: 13583 KB 上传时间: 2014-07-08 下载次数: 4 提 供 者: mei ...

  9. JDK源码解析 迭代器模式在JAVA的很多集合类中被广泛应用,接下来看看JAVA源码中是如何使用迭代器模式的。

    JDK源码解析 迭代器模式在JAVA的很多集合类中被广泛应用,接下来看看JAVA源码中是如何使用迭代器模式的. 看完这段代码是不是很熟悉,与我们上面代码基本类似.单列集合都使用到了迭代器,我们以Arr ...

最新文章

  1. MariaDB/MySQL备份和恢复(三):xtrabackup用法和原理详述
  2. 中科大提出统一输入过滤框架InFi:首次理论分析可过滤性,支持全数据模态
  3. python 周末大作业之2
  4. 什么是分布式锁及正确使用redis实现分布式锁
  5. LeetCode 1660. 纠正二叉树(BFS)
  6. JAVA原码反码补码
  7. dragstart drag dragend dragenter dragover dragleave drop
  8. android判断是华为手机,华为手机怎么辨别真假?华为手机真伪验证多种方法
  9. Go 依赖管理工具 Dep 的安装及配置
  10. sass-ihrm项目-系统用户权限设计概述-部门微服务、部门前端
  11. 执行SOA ——SOA实践指南
  12. [转帖]国之魂,民之魂(硬骨头六连16勇士的最后一张照片)
  13. UnityShader(四)基础光照
  14. BOM中的history对象和navigator对象
  15. leetcode每日一题·救生艇问题(Python)
  16. 阿里云DTS订阅实现实时运营服务的方案及注意事项
  17. concurrent.futures模块使用
  18. 【元胞自动机】元胞自动机双边教室疏散【含Matlab源码 1208期】
  19. 根据oe抓取ebayno title fits
  20. c语言切,c语言切换

热门文章

  1. ASPxGridView A primary key field specified via the KeyFieldName..
  2. 监控录像服务器性能要求,如何打造高性能的视频监控存储系统?(《中国安防》)...
  3. 使用jieba、pyhanlp工具实现关键字词句的提取
  4. Altium 学习笔记
  5. openGL library下载地址
  6. matlab伪随机数(以及如何得到真正的随机数)
  7. 碳排放的计算有多个版本
  8. 基于Conv3D和LSTM的出租车流量预测
  9. 完全用Linux(转)
  10. mysql中防呆是什么_防呆是什么意思