本地下载 qqwry.dat 

 

IPSeeker.java 用来读取QQwry.dat文件,以根据ip获位置信息;

注意:qqwry.dat在IPSeeker.java 文件中的路径;

  1 package com.sxt.etl.util.ip;
  2 
  3 import java.io.FileNotFoundException;
  4 import java.io.IOException;
  5 import java.io.RandomAccessFile;
  6 import java.io.UnsupportedEncodingException;
  7 import java.nio.ByteOrder;
  8 import java.nio.MappedByteBuffer;
  9 import java.nio.channels.FileChannel;
 10 import java.util.ArrayList;
 11 import java.util.Hashtable;
 12 import java.util.List;
 13 
 14 /**
 15  * * 用来读取QQwry.dat文件,以根据ip获得好友位置,QQwry.dat的格式是 一. 文件头,共8字节 1. 第一个起始IP的绝对偏移, 4字节
 16  * 2. 最后一个起始IP的绝对偏移, 4字节 二. "结束地址/国家/区域"记录区 四字节ip地址后跟的每一条记录分成两个部分 1. 国家记录 2.
 17  * 地区记录 但是地区记录是不一定有的。而且国家记录和地区记录都有两种形式 1. 以0结束的字符串 2. 4个字节,一个字节可能为0x1或0x2 a.
 18  * 为0x1时,表示在绝对偏移后还跟着一个区域的记录,注意是绝对偏移之后,而不是这四个字节之后 b. 为0x2时,表示在绝对偏移后没有区域记录
 19  * 不管为0x1还是0x2,后三个字节都是实际国家名的文件内绝对偏移
 20  * 如果是地区记录,0x1和0x2的含义不明,但是如果出现这两个字节,也肯定是跟着3个字节偏移,如果不是 则为0结尾字符串 三.
 21  * "起始地址/结束地址偏移"记录区 1. 每条记录7字节,按照起始地址从小到大排列 a. 起始IP地址,4字节 b. 结束ip地址的绝对偏移,3字节
 22  * 
 23  * 注意,这个文件里的ip地址和所有的偏移量均采用little-endian格式,而java是采用 big-endian格式的,要注意转换
 24  * 
 25  * @author root
 26  */
 27 public class IPSeeker {
 28     
 29     // 一些固定常量,比如记录长度等等
 30     private static final int IP_RECORD_LENGTH = 7;
 31     private static final byte AREA_FOLLOWED = 0x01;
 32     private static final byte NO_AREA = 0x2;
 33 
 34     // 用来做为cache,查询一个ip时首先查看cache,以减少不必要的重复查找
 35     private Hashtable ipCache;
 36     // 随机文件访问类
 37     private RandomAccessFile ipFile;
 38     // 内存映射文件
 39     private MappedByteBuffer mbb;
 40     // 单一模式实例
 41     private static IPSeeker instance = null;
 42     // 起始地区的开始和结束的绝对偏移
 43     private long ipBegin, ipEnd;
 44     // 为提高效率而采用的临时变量
 45     private IPLocation loc;
 46     private byte[] buf;
 47     private byte[] b4;
 48     private byte[] b3;
 49 
 50     /** */
 51     /**
 52      * 私有构造函数
 53      */
 54     protected IPSeeker() {
 55         ipCache = new Hashtable();
 56         loc = new IPLocation();
 57         buf = new byte[100];
 58         b4 = new byte[4];
 59         b3 = new byte[3];
 60         try {
 61             String ipFilePath = IPSeeker.class.getResource("/qqwry.dat")
 62                     .getFile();
 63             ipFile = new RandomAccessFile(ipFilePath, "r");
 64         } catch (FileNotFoundException e) {
 65             System.out.println("IP地址信息文件没有找到,IP显示功能将无法使用");
 66             ipFile = null;
 67 
 68         }
 69         // 如果打开文件成功,读取文件头信息
 70         if (ipFile != null) {
 71             try {
 72                 ipBegin = readLong4(0);
 73                 ipEnd = readLong4(4);
 74                 if (ipBegin == -1 || ipEnd == -1) {
 75                     ipFile.close();
 76                     ipFile = null;
 77                 }
 78             } catch (IOException e) {
 79                 System.out.println("IP地址信息文件格式有错误,IP显示功能将无法使用");
 80                 ipFile = null;
 81             }
 82         }
 83     }
 84 
 85     /** */
 86     /**
 87      * @return 单一实例
 88      */
 89     public static IPSeeker getInstance() {
 90         if (instance == null) {
 91             instance = new IPSeeker();
 92         }
 93         return instance;
 94     }
 95 
 96     /** */
 97     /**
 98      * 给定一个地点的不完全名字,得到一系列包含s子串的IP范围记录
 99      * 
100      * @param s
101      *            地点子串
102      * @return 包含IPEntry类型的List
103      */
104     public List getIPEntriesDebug(String s) {
105         List ret = new ArrayList();
106         long endOffset = ipEnd + 4;
107         for (long offset = ipBegin + 4; offset <= endOffset; offset += IP_RECORD_LENGTH) {
108             // 读取结束IP偏移
109             long temp = readLong3(offset);
110             // 如果temp不等于-1,读取IP的地点信息
111             if (temp != -1) {
112                 IPLocation loc = getIPLocation(temp);
113                 // 判断是否这个地点里面包含了s子串,如果包含了,添加这个记录到List中,如果没有,继续
114                 if (loc.country.indexOf(s) != -1 || loc.area.indexOf(s) != -1) {
115                     IPEntry entry = new IPEntry();
116                     entry.country = loc.country;
117                     entry.area = loc.area;
118                     // 得到起始IP
119                     readIP(offset - 4, b4);
120                     entry.beginIp = IPSeekerUtils.getIpStringFromBytes(b4);
121                     // 得到结束IP
122                     readIP(temp, b4);
123                     entry.endIp = IPSeekerUtils.getIpStringFromBytes(b4);
124                     // 添加该记录
125                     ret.add(entry);
126                 }
127             }
128         }
129         return ret;
130     }
131 
132     /** */
133     /**
134      * 给定一个地点的不完全名字,得到一系列包含s子串的IP范围记录
135      * 
136      * @param s
137      *            地点子串
138      * @return 包含IPEntry类型的List
139      */
140     public List getIPEntries(String s) {
141         List ret = new ArrayList();
142         try {
143             // 映射IP信息文件到内存中
144             if (mbb == null) {
145                 FileChannel fc = ipFile.getChannel();
146                 mbb = fc.map(FileChannel.MapMode.READ_ONLY, 0, ipFile.length());
147                 mbb.order(ByteOrder.LITTLE_ENDIAN);
148             }
149 
150             int endOffset = (int) ipEnd;
151             for (int offset = (int) ipBegin + 4; offset <= endOffset; offset += IP_RECORD_LENGTH) {
152                 int temp = readInt3(offset);
153                 if (temp != -1) {
154                     IPLocation loc = getIPLocation(temp);
155                     // 判断是否这个地点里面包含了s子串,如果包含了,添加这个记录到List中,如果没有,继续
156                     if (loc.country.indexOf(s) != -1
157                             || loc.area.indexOf(s) != -1) {
158                         IPEntry entry = new IPEntry();
159                         entry.country = loc.country;
160                         entry.area = loc.area;
161                         // 得到起始IP
162                         readIP(offset - 4, b4);
163                         entry.beginIp = IPSeekerUtils.getIpStringFromBytes(b4);
164                         // 得到结束IP
165                         readIP(temp, b4);
166                         entry.endIp = IPSeekerUtils.getIpStringFromBytes(b4);
167                         // 添加该记录
168                         ret.add(entry);
169                     }
170                 }
171             }
172         } catch (IOException e) {
173             System.out.println(e.getMessage());
174         }
175         return ret;
176     }
177 
178     /** */
179     /**
180      * 从内存映射文件的offset位置开始的3个字节读取一个int
181      * 
182      * @param offset
183      * @return
184      */
185     private int readInt3(int offset) {
186         mbb.position(offset);
187         return mbb.getInt() & 0x00FFFFFF;
188     }
189 
190     /** */
191     /**
192      * 从内存映射文件的当前位置开始的3个字节读取一个int
193      * 
194      * @return
195      */
196     private int readInt3() {
197         return mbb.getInt() & 0x00FFFFFF;
198     }
199 
200     /** */
201     /**
202      * 根据IP得到国家名
203      * 
204      * @param ip
205      *            ip的字节数组形式
206      * @return 国家名字符串
207      */
208     public String getCountry(byte[] ip) {
209         // 检查ip地址文件是否正常
210         if (ipFile == null)
211             return "错误的IP数据库文件";
212         // 保存ip,转换ip字节数组为字符串形式
213         String ipStr = IPSeekerUtils.getIpStringFromBytes(ip);
214         // 先检查cache中是否已经包含有这个ip的结果,没有再搜索文件
215         if (ipCache.containsKey(ipStr)) {
216             IPLocation loc = (IPLocation) ipCache.get(ipStr);
217             return loc.country;
218         } else {
219             IPLocation loc = getIPLocation(ip);
220             ipCache.put(ipStr, loc.getCopy());
221             return loc.country;
222         }
223     }
224 
225     /** */
226     /**
227      * 根据IP得到国家名
228      * 
229      * @param ip
230      *            IP的字符串形式
231      * @return 国家名字符串
232      */
233     public String getCountry(String ip) {
234         return getCountry(IPSeekerUtils.getIpByteArrayFromString(ip));
235     }
236 
237     /** */
238     /**
239      * 根据IP得到地区名
240      * 
241      * @param ip
242      *            ip的字节数组形式
243      * @return 地区名字符串
244      */
245     public String getArea(byte[] ip) {
246         // 检查ip地址文件是否正常
247         if (ipFile == null)
248             return "错误的IP数据库文件";
249         // 保存ip,转换ip字节数组为字符串形式
250         String ipStr = IPSeekerUtils.getIpStringFromBytes(ip);
251         // 先检查cache中是否已经包含有这个ip的结果,没有再搜索文件
252         if (ipCache.containsKey(ipStr)) {
253             IPLocation loc = (IPLocation) ipCache.get(ipStr);
254             return loc.area;
255         } else {
256             IPLocation loc = getIPLocation(ip);
257             ipCache.put(ipStr, loc.getCopy());
258             return loc.area;
259         }
260     }
261 
262     /**
263      * 根据IP得到地区名
264      * 
265      * @param ip
266      *            IP的字符串形式
267      * @return 地区名字符串
268      */
269     public String getArea(String ip) {
270         return getArea(IPSeekerUtils.getIpByteArrayFromString(ip));
271     }
272 
273     /** */
274     /**
275      * 根据ip搜索ip信息文件,得到IPLocation结构,所搜索的ip参数从类成员ip中得到
276      * 
277      * @param ip
278      *            要查询的IP
279      * @return IPLocation结构
280      */
281     public IPLocation getIPLocation(byte[] ip) {
282         IPLocation info = null;
283         long offset = locateIP(ip);
284         if (offset != -1)
285             info = getIPLocation(offset);
286         if (info == null) {
287             info = new IPLocation();
288             info.country = "未知国家";
289             info.area = "未知地区";
290         }
291         return info;
292     }
293 
294     /**
295      * 从offset位置读取4个字节为一个long,因为java为big-endian格式,所以没办法 用了这么一个函数来做转换
296      * 
297      * @param offset
298      * @return 读取的long值,返回-1表示读取文件失败
299      */
300     private long readLong4(long offset) {
301         long ret = 0;
302         try {
303             ipFile.seek(offset);
304             ret |= (ipFile.readByte() & 0xFF);
305             ret |= ((ipFile.readByte() << 8) & 0xFF00);
306             ret |= ((ipFile.readByte() << 16) & 0xFF0000);
307             ret |= ((ipFile.readByte() << 24) & 0xFF000000);
308             return ret;
309         } catch (IOException e) {
310             return -1;
311         }
312     }
313 
314     /**
315      * 从offset位置读取3个字节为一个long,因为java为big-endian格式,所以没办法 用了这么一个函数来做转换
316      * 
317      * @param offset
318      * @return 读取的long值,返回-1表示读取文件失败
319      */
320     private long readLong3(long offset) {
321         long ret = 0;
322         try {
323             ipFile.seek(offset);
324             ipFile.readFully(b3);
325             ret |= (b3[0] & 0xFF);
326             ret |= ((b3[1] << 8) & 0xFF00);
327             ret |= ((b3[2] << 16) & 0xFF0000);
328             return ret;
329         } catch (IOException e) {
330             return -1;
331         }
332     }
333 
334     /**
335      * 从当前位置读取3个字节转换成long
336      * 
337      * @return
338      */
339     private long readLong3() {
340         long ret = 0;
341         try {
342             ipFile.readFully(b3);
343             ret |= (b3[0] & 0xFF);
344             ret |= ((b3[1] << 8) & 0xFF00);
345             ret |= ((b3[2] << 16) & 0xFF0000);
346             return ret;
347         } catch (IOException e) {
348             return -1;
349         }
350     }
351 
352     /**
353      * 从offset位置读取四个字节的ip地址放入ip数组中,读取后的ip为big-endian格式,但是
354      * 文件中是little-endian形式,将会进行转换
355      * 
356      * @param offset
357      * @param ip
358      */
359     private void readIP(long offset, byte[] ip) {
360         try {
361             ipFile.seek(offset);
362             ipFile.readFully(ip);
363             byte temp = ip[0];
364             ip[0] = ip[3];
365             ip[3] = temp;
366             temp = ip[1];
367             ip[1] = ip[2];
368             ip[2] = temp;
369         } catch (IOException e) {
370             System.out.println(e.getMessage());
371         }
372     }
373 
374     /**
375      * 从offset位置读取四个字节的ip地址放入ip数组中,读取后的ip为big-endian格式,但是
376      * 文件中是little-endian形式,将会进行转换
377      * 
378      * @param offset
379      * @param ip
380      */
381     private void readIP(int offset, byte[] ip) {
382         mbb.position(offset);
383         mbb.get(ip);
384         byte temp = ip[0];
385         ip[0] = ip[3];
386         ip[3] = temp;
387         temp = ip[1];
388         ip[1] = ip[2];
389         ip[2] = temp;
390     }
391 
392     /**
393      * 把类成员ip和beginIp比较,注意这个beginIp是big-endian的
394      * 
395      * @param ip
396      *            要查询的IP
397      * @param beginIp
398      *            和被查询IP相比较的IP
399      * @return 相等返回0,ip大于beginIp则返回1,小于返回-1。
400      */
401     private int compareIP(byte[] ip, byte[] beginIp) {
402         for (int i = 0; i < 4; i++) {
403             int r = compareByte(ip[i], beginIp[i]);
404             if (r != 0)
405                 return r;
406         }
407         return 0;
408     }
409 
410     /**
411      * 把两个byte当作无符号数进行比较
412      * 
413      * @param b1
414      * @param b2
415      * @return 若b1大于b2则返回1,相等返回0,小于返回-1
416      */
417     private int compareByte(byte b1, byte b2) {
418         if ((b1 & 0xFF) > (b2 & 0xFF)) // 比较是否大于
419             return 1;
420         else if ((b1 ^ b2) == 0)// 判断是否相等
421             return 0;
422         else
423             return -1;
424     }
425 
426     /**
427      * 这个方法将根据ip的内容,定位到包含这个ip国家地区的记录处,返回一个绝对偏移 方法使用二分法查找。
428      * 
429      * @param ip
430      *            要查询的IP
431      * @return 如果找到了,返回结束IP的偏移,如果没有找到,返回-1
432      */
433     private long locateIP(byte[] ip) {
434         long m = 0;
435         int r;
436         // 比较第一个ip项
437         readIP(ipBegin, b4);
438         r = compareIP(ip, b4);
439         if (r == 0)
440             return ipBegin;
441         else if (r < 0)
442             return -1;
443         // 开始二分搜索
444         for (long i = ipBegin, j = ipEnd; i < j;) {
445             m = getMiddleOffset(i, j);
446             readIP(m, b4);
447             r = compareIP(ip, b4);
448             // log.debug(Utils.getIpStringFromBytes(b));
449             if (r > 0)
450                 i = m;
451             else if (r < 0) {
452                 if (m == j) {
453                     j -= IP_RECORD_LENGTH;
454                     m = j;
455                 } else
456                     j = m;
457             } else
458                 return readLong3(m + 4);
459         }
460         // 如果循环结束了,那么i和j必定是相等的,这个记录为最可能的记录,但是并非
461         // 肯定就是,还要检查一下,如果是,就返回结束地址区的绝对偏移
462         m = readLong3(m + 4);
463         readIP(m, b4);
464         r = compareIP(ip, b4);
465         if (r <= 0)
466             return m;
467         else
468             return -1;
469     }
470 
471     /**
472      * 得到begin偏移和end偏移中间位置记录的偏移
473      * 
474      * @param begin
475      * @param end
476      * @return
477      */
478     private long getMiddleOffset(long begin, long end) {
479         long records = (end - begin) / IP_RECORD_LENGTH;
480         records >>= 1;
481         if (records == 0)
482             records = 1;
483         return begin + records * IP_RECORD_LENGTH;
484     }
485 
486     /**
487      * 给定一个ip国家地区记录的偏移,返回一个IPLocation结构
488      * 
489      * @param offset
490      * @return
491      */
492     private IPLocation getIPLocation(long offset) {
493         try {
494             // 跳过4字节ip
495             ipFile.seek(offset + 4);
496             // 读取第一个字节判断是否标志字节
497             byte b = ipFile.readByte();
498             if (b == AREA_FOLLOWED) {
499                 // 读取国家偏移
500                 long countryOffset = readLong3();
501                 // 跳转至偏移处
502                 ipFile.seek(countryOffset);
503                 // 再检查一次标志字节,因为这个时候这个地方仍然可能是个重定向
504                 b = ipFile.readByte();
505                 if (b == NO_AREA) {
506                     loc.country = readString(readLong3());
507                     ipFile.seek(countryOffset + 4);
508                 } else
509                     loc.country = readString(countryOffset);
510                 // 读取地区标志
511                 loc.area = readArea(ipFile.getFilePointer());
512             } else if (b == NO_AREA) {
513                 loc.country = readString(readLong3());
514                 loc.area = readArea(offset + 8);
515             } else {
516                 loc.country = readString(ipFile.getFilePointer() - 1);
517                 loc.area = readArea(ipFile.getFilePointer());
518             }
519             return loc;
520         } catch (IOException e) {
521             return null;
522         }
523     }
524 
525     /**
526      * @param offset
527      * @return
528      */
529     private IPLocation getIPLocation(int offset) {
530         // 跳过4字节ip
531         mbb.position(offset + 4);
532         // 读取第一个字节判断是否标志字节
533         byte b = mbb.get();
534         if (b == AREA_FOLLOWED) {
535             // 读取国家偏移
536             int countryOffset = readInt3();
537             // 跳转至偏移处
538             mbb.position(countryOffset);
539             // 再检查一次标志字节,因为这个时候这个地方仍然可能是个重定向
540             b = mbb.get();
541             if (b == NO_AREA) {
542                 loc.country = readString(readInt3());
543                 mbb.position(countryOffset + 4);
544             } else
545                 loc.country = readString(countryOffset);
546             // 读取地区标志
547             loc.area = readArea(mbb.position());
548         } else if (b == NO_AREA) {
549             loc.country = readString(readInt3());
550             loc.area = readArea(offset + 8);
551         } else {
552             loc.country = readString(mbb.position() - 1);
553             loc.area = readArea(mbb.position());
554         }
555         return loc;
556     }
557 
558     /**
559      * 从offset偏移开始解析后面的字节,读出一个地区名
560      * 
561      * @param offset
562      * @return 地区名字符串
563      * @throws IOException
564      */
565     private String readArea(long offset) throws IOException {
566         ipFile.seek(offset);
567         byte b = ipFile.readByte();
568         if (b == 0x01 || b == 0x02) {
569             long areaOffset = readLong3(offset + 1);
570             if (areaOffset == 0)
571                 return "未知地区";
572             else
573                 return readString(areaOffset);
574         } else
575             return readString(offset);
576     }
577 
578     /**
579      * @param offset
580      * @return
581      */
582     private String readArea(int offset) {
583         mbb.position(offset);
584         byte b = mbb.get();
585         if (b == 0x01 || b == 0x02) {
586             int areaOffset = readInt3();
587             if (areaOffset == 0)
588                 return "未知地区";
589             else
590                 return readString(areaOffset);
591         } else
592             return readString(offset);
593     }
594 
595     /**
596      * 从offset偏移处读取一个以0结束的字符串
597      * 
598      * @param offset
599      * @return 读取的字符串,出错返回空字符串
600      */
601     private String readString(long offset) {
602         try {
603             ipFile.seek(offset);
604             int i;
605             for (i = 0, buf[i] = ipFile.readByte(); buf[i] != 0; buf[++i] = ipFile
606                     .readByte())
607                 ;
608             if (i != 0)
609                 return IPSeekerUtils.getString(buf, 0, i, "GBK");
610         } catch (IOException e) {
611             System.out.println(e.getMessage());
612         }
613         return "";
614     }
615 
616     /**
617      * 从内存映射文件的offset位置得到一个0结尾字符串
618      * 
619      * @param offset
620      * @return
621      */
622     private String readString(int offset) {
623         try {
624             mbb.position(offset);
625             int i;
626             for (i = 0, buf[i] = mbb.get(); buf[i] != 0; buf[++i] = mbb.get())
627                 ;
628             if (i != 0)
629                 return IPSeekerUtils.getString(buf, 0, i, "GBK");
630         } catch (IllegalArgumentException e) {
631             System.out.println(e.getMessage());
632         }
633         return "";
634     }
635 
636     public String getAddress(String ip) {
637         String country = getCountry(ip).equals(" CZ88.NET") ? ""
638                 : getCountry(ip);
639         String area = getArea(ip).equals(" CZ88.NET") ? "" : getArea(ip);
640         String address = country + " " + area;
641         return address.trim();
642     }
643 
644     /**
645      * * 用来封装ip相关信息,目前只有两个字段,ip所在的国家和地区
646      * 
647      * 
648      * @author swallow
649      */
650     public class IPLocation {
651         public String country;
652         public String area;
653 
654         public IPLocation() {
655             country = area = "";
656         }
657 
658         public IPLocation getCopy() {
659             IPLocation ret = new IPLocation();
660             ret.country = country;
661             ret.area = area;
662             return ret;
663         }
664     }
665 
666     /**
667      * 一条IP范围记录,不仅包括国家和区域,也包括起始IP和结束IP *
668      * 
669      * 
670      * @author root
671      */
672     public class IPEntry {
673         public String beginIp;
674         public String endIp;
675         public String country;
676         public String area;
677 
678         public IPEntry() {
679             beginIp = endIp = country = area = "";
680         }
681 
682         public String toString() {
683             return this.area + " " + this.country + "IP  Χ:" + this.beginIp
684                     + "-" + this.endIp;
685         }
686     }
687 
688     /**
689      * 操作工具类
690      * 
691      * @author root
692      * 
693      */
694     public static class IPSeekerUtils {
695         /**
696          * 从ip的字符串形式得到字节数组形式
697          * 
698          * @param ip
699          *            字符串形式的ip
700          * @return 字节数组形式的ip
701          */
702         public static byte[] getIpByteArrayFromString(String ip) {
703             byte[] ret = new byte[4];
704             java.util.StringTokenizer st = new java.util.StringTokenizer(ip,
705                     ".");
706             try {
707                 ret[0] = (byte) (Integer.parseInt(st.nextToken()) & 0xFF);
708                 ret[1] = (byte) (Integer.parseInt(st.nextToken()) & 0xFF);
709                 ret[2] = (byte) (Integer.parseInt(st.nextToken()) & 0xFF);
710                 ret[3] = (byte) (Integer.parseInt(st.nextToken()) & 0xFF);
711             } catch (Exception e) {
712                 System.out.println(e.getMessage());
713             }
714             return ret;
715         }
716 
717         /**
718          * 对原始字符串进行编码转换,如果失败,返回原始的字符串
719          * 
720          * @param s
721          *            原始字符串
722          * @param srcEncoding
723          *            源编码方式
724          * @param destEncoding
725          *            目标编码方式
726          * @return 转换编码后的字符串,失败返回原始字符串
727          */
728         public static String getString(String s, String srcEncoding,
729                 String destEncoding) {
730             try {
731                 return new String(s.getBytes(srcEncoding), destEncoding);
732             } catch (UnsupportedEncodingException e) {
733                 return s;
734             }
735         }
736 
737         /**
738          * 根据某种编码方式将字节数组转换成字符串
739          * 
740          * @param b
741          *            字节数组
742          * @param encoding
743          *            编码方式
744          * @return 如果encoding不支持,返回一个缺省编码的字符串
745          */
746         public static String getString(byte[] b, String encoding) {
747             try {
748                 return new String(b, encoding);
749             } catch (UnsupportedEncodingException e) {
750                 return new String(b);
751             }
752         }
753 
754         /**
755          * 根据某种编码方式将字节数组转换成字符串
756          * 
757          * @param b
758          *            字节数组
759          * @param offset
760          *            要转换的起始位置
761          * @param len
762          *            要转换的长度
763          * @param encoding
764          *            编码方式
765          * @return 如果encoding不支持,返回一个缺省编码的字符串
766          */
767         public static String getString(byte[] b, int offset, int len,
768                 String encoding) {
769             try {
770                 return new String(b, offset, len, encoding);
771             } catch (UnsupportedEncodingException e) {
772                 return new String(b, offset, len);
773             }
774         }
775 
776         /**
777          * @param ip
778          *            ip的字节数组形式
779          * @return 字符串形式的ip
780          */
781         public static String getIpStringFromBytes(byte[] ip) {
782             StringBuffer sb = new StringBuffer();
783             sb.append(ip[0] & 0xFF);
784             sb.append('.');
785             sb.append(ip[1] & 0xFF);
786             sb.append('.');
787             sb.append(ip[2] & 0xFF);
788             sb.append('.');
789             sb.append(ip[3] & 0xFF);
790             return sb.toString();
791         }
792     }
793 
794     /**
795      * 获取全部ip地址集合列表
796      * 
797      * @return
798      */
799     public List<String> getAllIp() {
800         List<String> list = new ArrayList<String>();
801         byte[] buf = new byte[4];
802         for (long i = ipBegin; i < ipEnd; i += IP_RECORD_LENGTH) {
803             try {
804                 this.readIP(this.readLong3(i + 4), buf); // 读取ip,最终ip放到buf中
805                 String ip = IPSeekerUtils.getIpStringFromBytes(buf);
806                 list.add(ip);
807             } catch (Exception e) {
808                 // nothing
809             }
810         }
811         return list;
812     }
813 }
View Code

 

IPSeekerExt.java 继承IPSeeker.java ,将解析的结果做了进一步优化;

eg: 北京, 北京市

  1 package com.sxt.etl.util;
  2 
  3 import com.sxt.common.GlobalConstants;
  4 import com.sxt.etl.util.ip.IPSeeker;
  5 
  6 /**
  7  * 定义具体的ip解析的类,最终调用IpSeeker类(父类)<br/>
  8  * 解析ip最终的返回时:国家名称 省份名称 城市名称<br/>
  9  * 如果是国外的ip,那么直接设置为unknown unknown unknown<br/>
 10  * 如果是国内ip,如果没法进行解析,那么就设置为中国 unknown unknown<br/>
 11  * 
 12  * @author root
 13  *
 14  */
 15 public class IPSeekerExt extends IPSeeker {
 16     private RegionInfo DEFAULT_INFO = new RegionInfo();
 17 
 18     /**
 19      * 解析ip地址,返回该ip地址对应的国家省份信息<br/>
 20      * 如果该ip解析失败,那么直接返回默认值
 21      * 
 22      * @param ip
 23      *            要解析的ip地址,格式为: 120.197.87.216
 24      * @return
 25      */
 26     public RegionInfo analyticIp(String ip) {
 27         if (ip == null || ip.trim().isEmpty()) {
 28             return DEFAULT_INFO;
 29         }
 30 
 31         RegionInfo info = new RegionInfo();
 32         try {
 33             String country = super.getCountry(ip);
 34             if ("局域网".equals(country)) {
 35                 info.setCountry("中国");
 36                 info.setProvince("上海市");
 37             } else if (country != null && !country.trim().isEmpty()) {
 38                 // 表示该ip还一个可以解析的ip
 39                 country = country.trim();
 40                 int length = country.length();
 41                 int index = country.indexOf('省');
 42                 if (index > 0) {
 43                     // 当前ip属于23个省之间的一个,country的格式为:xxx省(xxx市)(xxx县/区)
 44                     info.setCountry("中国");
 45                     if (index == length - 1) {
 46                         info.setProvince(country); // 设置省份,格式列入: 广东省
 47                     } else {
 48                         // 格式为:广东省广州市
 49                         info.setProvince(country.substring(0, index + 1)); // 设置省份
 50                         int index2 = country.indexOf('市', index); // 查看下一个出现市的位置
 51                         if (index2 > 0) {
 52                             country.substring(1, 1);
 53                             info.setCity(country.substring(index + 1,
 54                                     Math.min(index2 + 1, length))); // 设置city
 55                         }
 56                     }
 57                 } else {
 58                     // 其他的五个自治区 四个直辖市 2个特别行政区
 59                     String flag = country.substring(0, 2); // 拿字符串前两位
 60                     switch (flag) {
 61                     case "内蒙":
 62                         info.setCountry("中国");
 63                         info.setProvince("内蒙古自治区");
 64                         country = country.substring(3);
 65                         if (country != null && !country.isEmpty()) {
 66                             index = country.indexOf('市');
 67                             if (index > 0) {
 68                                 info.setCity(country.substring(0,
 69                                         Math.min(index + 1, country.length()))); // 设置市
 70                             }
 71                         }
 72                         break;
 73                     case "广西":
 74                     case "西藏":
 75                     case "宁夏":
 76                     case "新疆":
 77                         info.setCountry("中国");
 78                         info.setProvince(flag);
 79                         country = country.substring(2);
 80                         if (country != null && !country.isEmpty()) {
 81                             index = country.indexOf('市');
 82                             if (index > 0) {
 83                                 info.setCity(country.substring(0,
 84                                         Math.min(index + 1, country.length()))); // 设置市
 85                             }
 86                         }
 87                         break;
 88                     case "上海":
 89                     case "北京":
 90                     case "天津":
 91                     case "重庆":
 92                         info.setCountry("中国");
 93                         info.setProvince(flag + "市");
 94                         country = country.substring(3); // 去除这个省份/直辖市
 95                         if (country != null && !country.isEmpty()) {
 96                             index = country.indexOf('区');
 97                             if (index > 0) {
 98                                 char ch = country.charAt(index - 1);
 99                                 if (ch != '校' || ch != '小') {
100                                     info.setCity(country.substring(
101                                             0,
102                                             Math.min(index + 1,
103                                                     country.length()))); // 设置区
104                                 }
105                             }
106 
107                             if (RegionInfo.DEFAULT_VALUE.equals(info.getCity())) {
108                                 // city还是默认值
109                                 index = country.indexOf('县');
110                                 if (index > 0) {
111                                     info.setCity(country.substring(
112                                             0,
113                                             Math.min(index + 1,
114                                                     country.length()))); // 设置区
115                                 }
116                             }
117                         }
118                         break;
119                     case "香港":
120                     case "澳门":
121                         info.setCountry("中国");
122                         info.setProvince(flag + "特别行政区");
123                         break;
124                     default:
125                         break;
126                     }
127                 }
128             }
129         } catch (Exception e) {
130             // 解析过程中出现异常
131             e.printStackTrace();
132         }
133         return info;
134     }
135 
136     /**
137      * ip地域相关的一个model
138      * 
139      * @author root
140      *
141      */
142     public static class RegionInfo {
143         public static final String DEFAULT_VALUE = GlobalConstants.DEFAULT_VALUE; // 默认值
144         private String country = DEFAULT_VALUE; // 国家
145         private String province = DEFAULT_VALUE; // 省份
146         private String city = DEFAULT_VALUE; // 城市
147 
148         public String getCountry() {
149             return country;
150         }
151 
152         public void setCountry(String country) {
153             this.country = country;
154         }
155 
156         public String getProvince() {
157             return province;
158         }
159 
160         public void setProvince(String province) {
161             this.province = province;
162         }
163 
164         public String getCity() {
165             return city;
166         }
167 
168         public void setCity(String city) {
169             this.city = city;
170         }
171 
172         @Override
173         public String toString() {
174             return "RegionInfo [country=" + country + ", province=" + province
175                     + ", city=" + city + "]";
176         }
177     }
178 }
View Code

 

package com.sxt.test;

import java.util.List;

import com.sxt.etl.util.IPSeekerExt;
import com.sxt.etl.util.IPSeekerExt.RegionInfo;


public class TestIPSeekerExt {
    public static void main(String[] args) {
        IPSeekerExt ipSeekerExt = new IPSeekerExt();
        RegionInfo info = ipSeekerExt.analyticIp("114.114.114.114");
        System.out.println(info);

//        List<String> ips = ipSeekerExt.getAllIp();
//        for (String ip : ips) {
//            System.out.println(ip + " --- " + ipSeekerExt.analyticIp(ip));
//        }
    }
}

 

大数据>0722>20170722>项目>BIG_DATA_SXT_1