Terry.Li-彬
虚其心,可解天下之问;专其心,可治天下之学;静其心,可悟天下之理;恒其心,可成天下之业。
BlogJava
::
首页
::
新随笔
::
联系
::
聚合
::
管理
::
143 随笔 :: 344 文章 :: 130 评论 :: 0 Trackbacks
<
2014年4月
>
日
一
二
三
四
五
六
30
31
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
1
2
3
4
5
6
7
8
9
10
常用链接
我的随笔
我的文章
我的评论
我的参与
最新评论
留言簿
(19)
给我留言
查看公开留言
查看私人留言
随笔分类
(107)
CA(16)
(rss)
Extremecomponents (1)
(rss)
ibatis(4)
(rss)
Jakarta(9)
(rss)
Java(19)
(rss)
Liferay(21)
(rss)
maven2(15)
(rss)
postgresql(2)
(rss)
sitemesh
(rss)
spring2.0
(rss)
struts2.0
(rss)
struts-menu(1)
(rss)
webservice(17)
(rss)
设计模式(2)
(rss)
随笔档案
(141)
2017年4月 (1)
2013年3月 (1)
2013年1月 (1)
2009年12月 (1)
2009年9月 (3)
2009年2月 (1)
2008年12月 (2)
2008年11月 (1)
2008年9月 (2)
2008年8月 (2)
2008年5月 (1)
2008年3月 (22)
2008年2月 (34)
2008年1月 (14)
2007年12月 (7)
2007年11月 (34)
2007年10月 (1)
2007年9月 (13)
文章分类
(284)
ActiveMQ(7)
(rss)
Ajax(5)
(rss)
Axis(21)
(rss)
BootStrap(4)
(rss)
cache(7)
(rss)
chat(1)
(rss)
css
(rss)
DataBase(5)
(rss)
Dwr(3)
(rss)
ejb3.0(4)
(rss)
ESB(3)
(rss)
ESB(4)
(rss)
flex3(1)
(rss)
Freemarker(2)
(rss)
Hibernate Search(2)
(rss)
ibatis + spring (11)
(rss)
java(44)
(rss)
javascript(8)
(rss)
JBoss(5)
(rss)
Jbpm(18)
(rss)
JBPM4(6)
(rss)
jetty
(rss)
JMS(6)
(rss)
JMX(1)
(rss)
JPA(1)
(rss)
jquery(7)
(rss)
Jsp(5)
(rss)
Liferay(5)
(rss)
Linux(12)
(rss)
Log(6)
(rss)
Maven2(10)
(rss)
MyBatis
(rss)
mysql(4)
(rss)
soa(1)
(rss)
soap(5)
(rss)
spring(7)
(rss)
Spring Security(2)
(rss)
SSH(3)
(rss)
struts2.0(7)
(rss)
svn(2)
(rss)
web service(20)
(rss)
WebWork(1)
(rss)
Web前端(2)
(rss)
wireless(1)
(rss)
wsdl(1)
(rss)
yav(1)
(rss)
报表(3)
(rss)
推荐引擎(2)
(rss)
模板(5)
(rss)
设计模式(2)
(rss)
通信(1)
(rss)
文章档案
(342)
2015年9月 (2)
2013年12月 (1)
2013年11月 (2)
2013年10月 (2)
2013年9月 (1)
2013年8月 (1)
2013年3月 (3)
2013年2月 (1)
2013年1月 (17)
2011年8月 (4)
2011年7月 (1)
2011年6月 (6)
2011年5月 (2)
2011年4月 (2)
2011年3月 (4)
2011年2月 (2)
2011年1月 (3)
2010年12月 (8)
2010年11月 (10)
2010年10月 (7)
2010年9月 (32)
2010年8月 (6)
2010年7月 (1)
2010年6月 (2)
2010年5月 (1)
2010年4月 (1)
2010年3月 (1)
2010年2月 (1)
2010年1月 (2)
2009年12月 (1)
2009年11月 (1)
2009年10月 (5)
2009年9月 (23)
2009年8月 (6)
2009年7月 (18)
2009年6月 (2)
2009年5月 (5)
2009年4月 (5)
2009年2月 (9)
2009年1月 (19)
2008年12月 (13)
2008年10月 (1)
2008年9月 (3)
2008年8月 (2)
2008年7月 (5)
2008年6月 (5)
2008年5月 (3)
2008年4月 (6)
2007年12月 (9)
2007年11月 (32)
2007年10月 (2)
2007年9月 (41)
相册
北京建工测试数据库脚本
我的相册
收藏夹
(58)
我的收藏(58)
(rss)
家装
★榻榻米卡座衣帽间★田园暖家硬装完毕上软装咯
淘金币韩版短款小棉袄甜美棉服拉链厚外套面包棉衣秋冬装女装新款
最新随笔
1. 身份证JS校验
2. MySQL导入.sql文件及常用命令
3. ERROR 2006 (HY000) at line xx: MySQL server has gone away 解决方法
4. Ajax Session Timeout 超时 处理
5. 忘掉jQuery,使用JavaScript原生API
6. Ehcache详细解读
7. How can I print SQL query result log with log4j?
8. repositories
9. ehcache 配置参数说明
10. OSChina架构中,依照“管理重于配置”的思路使用Servlet设计的MVC
11. class卸载、热替换和Tomcat的热部署的分析
12. 个性化推荐技术漫谈
13. 探索推荐引擎内部的秘密,第 1 部分: 推荐引擎初探
14. 一个类引发的回忆
15. 配置_Druid和Spring关联监控配置
16. AtomicInteger
17. spring通过annotation注册MBean到JMX
18. BootStrap入门教程 (四)
19. BootStrap入门教程 (三)
20. BootStrap入门教程 (二)
21. BootStrap入门教程 (一)
22. Linux centos 6.3 Tomcat Install
23. Linux centos 6.3 Nginx Install
24. Linux centos 6.3 Python Install
25. Linux centos 6.3 Redis Install
26. Linux centos 6.3 Mysql Install
27. Linux centos 6.3 Mysql slave Install
28. Linux centos 6.3 Lua Install
29. Linux centos 6.3 Memcached Install
30. Linux centos 6.3 Erlang Install
31. Linux centos 6.3 Graphicsmagick Install
32. Linux centos 6.3 Ejabberd Install
33. Linux centos 6.3 JDK Install
34. flex摄像头拍照 java上传到数据库 .
35. Nginx+Tomcat+Memcached共享session集群配置
36. 基于词典的正向最大匹配中文分词算法,能实现中英文数字混合分词
37. linux下Nginx+tomcat整合的安装与配置
38. 从 iBatis 到 MyBatis - MyBatis 简明学习教程
39. Apache Http Server与Tomcat实现负载均衡和集群
40. linux+nginx+tomcat负载均衡,实现session同步
搜索
积分与排名
积分 - 740098
排名 - 61
最新评论
1. re: 配置_Druid和Spring关联监控配置
fchgvhbjnkmll
--hb
2. re: eclipse 属性文件,自动转为Unicode编码[未登录]
哈哈
--哈哈
3. re: Ehcache详细解读[未登录]
话说都是这一篇文章,有意思
--啊
4. re: Maven使用deploy命令部署构建
感谢!我也碰到这个问题了!
--曲奇饼
5. re: Apache Http Server与Tomcat实现负载均衡和集群[未登录]
讲的很详细,受教了
--林
6. re: 转载 高负载系统架构设计
顶,但是,在数据分离方面好像写的很模糊!
--nqfang
7. re: Ajax Session Timeout 超时 处理
sads
--11
8. 354554
684854986
--3498665
9. re: 将任意格式转换为JSON数据格式的工具类
["d":“气而去”]
--人
10. re: FreeMarker 自定义标签
评论内容较长,点击标题查看
--zuidaima
11. re: Maven使用deploy命令部署构建[未登录]
好,写的比较详细,按此配置成功。
--Anderson
12. re: BootStrap入门教程 (三)
评论内容较长,点击标题查看
--zuidaima
13. re: (转载)页面静态化(JSP动态页面转静态化)
@静态化
伪静态化,应该使用模板生成HTML
--libin2722
14. re: (转载)页面静态化(JSP动态页面转静态化)
这个不是静态化,就不要用静态化的标题吓人!!!
--静态化
15. re: BootStrap入门教程 (二)
评论内容较长,点击标题查看
--zuidaima
16. re: 权限控制:spring 3.0 security配置例子
谢谢,遇到了最后的问题,还好找到答案解决了~
--ZL
17. re: BootStrap入门教程 (一)
评论内容较长,点击标题查看
--zuidaima
18. re: BootStrap入门教程 (三)
评论内容较长,点击标题查看
--zuidaima
19. re: 配置_Druid和Spring关联监控配置[未登录]
sasa
--aaa
20. re: JAVA解析纯真IP地址库
你这个有多线程并发问题
--苏秦
21. re: flex摄像头拍照 java上传到数据库 .
评论内容较长,点击标题查看
--123yedddd
22. re: 在基于Spring及Hibernate应用程序中使用ETags降低带宽占用和服务器压力
评论内容较长,点击标题查看
--最代码
23. re: Apache Http Server与Tomcat实现负载均衡和集群
集群后,创建的 test1.jsp 放在哪儿?
--秦真
24. re: BootStrap入门教程 (一)
作为入门教程,连需要包含的css都不提。。。
--作为入门教程,连需要包含的css都不提。。。
25. re: BootStrap入门教程 (一)[未登录]
作为入门教程,连需要包含的css都不提。。。
--bluefield
26. re: url加密参数 (java版)
asd飞
--是电饭锅
27. re: 将JBoss启动做成Windows的系统服务
我的服务也起不来
--23
28. re: 测试-答对5道题的人是天才,答对4道的是帅才,答对3道的是将才,答对2道的是奇才,答对1道的是人才
3、冲
4、“亼”或者“亽”
5、129
--byl
29. re: JBPM数据库表说明
这些表全部手工创建吗?
--轻描淡写
30. re: 将JBoss启动做成Windows的系统服务
为什么我做的服务起不来呢?
--吴江
31. re: 利用开源组件制作验证码 Captcha
ggg
--ssss
32. re: 基于词典的正向最大匹配中文分词算法,能实现中英文数字混合分词
麻烦 博主把dictionary.txt发给我吧,让我学习学习
1182787467@qq.com
谢谢
--love code
33. re: 权限控制:spring 3.0 security配置例子[未登录]
楼主真的很细心 我是在国外论坛上发现了同样的问题
--tommy
34. re: 基于词典的正向最大匹配中文分词算法,能实现中英文数字混合分词
这是全的么,楼主?我导入到MyEclipse里好多错误呀,除了import包之外还有好多错,看不懂。。。
--yi
35. re: FreeMarker 自定义标签
感谢!
--四海
36. re: WebWork 2.2 + Tomcat 5.0 + Jdk 1.6 出现Provider org.apache.xalan.processor.TransformerFactoryImpl not found错误
我tomcat1.60+jdk1.60也出现这个问题,网上度了下,加个xalan.jar包就好了
--liuruliu
37. re: FreeMarker 自定义标签[未登录]
copy 别人文章也不带个链接.
--selina
38. re: 权限控制:spring 3.0 security配置例子[未登录]
最后那点确实坑爹啊。。
--123
39. re: 图片加载错误,显示默认图片
奋斗奋斗
--分段
40. re: JBPM数据库表说明[未登录]
非常感谢
--nicol
阅读排行榜
1. 打印出Ibatis最终的SQL语句(8709)
2. server-config.wsdd配置一例(8457)
3. 编写你自己的单点登录(SSO)服务 (8388)
4. Axis1.4 利用 deploy.wsdd 发布 server-config.wsdd文件(6980)
5. HashMap 、HashTable、HashSet的区别 (4906)
6. ClassNotFoundException: org.hibernate.hql.ast.HqlToken(4663)
7. Axis1.4 开发笔记(3720)
8. Liferay Portal二次开发指南(3648)
9. ibatis中文与like的问题 (3466)
10. 一个服务返回一个ArrayList,如何使用Axis序列化/反序列化啊(3447)
11. Maven2 常用命令(3340)
12. 使用maven2 打ear包(3216)
13. Apache Maven 2 简介(最全的文档)(3201)
14. httpclient中MultipartPostMethod类上传文件(2909)
15. Double:双精度类型(2454)
16. 使用Java实现CA(一)(2350)
17. JAVA中SSL证书认证通讯-Client(2330)
18. 深入了解Java ClassLoader、Bytecode 、ASM、cglib (2266)
19. 在Struts2.0中如何得到绝对路径(2212)
20. 将JBoss启动做成Windows的系统服务(2048)
21. 一个可以在页面上随意画线、多边形、圆,填充等功能的js (1967)
22. Struts2.0 中配置 Struts-Menu(1923)
23. JSF最佳入门(1882)
24. 深入浅出Liferay Portal (4) (1806)
25. 教程--开始使用Maven下(1726)
26. portal专题(一)用liferay server简单开发portlet快速上手(1594)
27. 深入浅出Liferay Portal (10) (1587)
28. 用axis发布webservices(一) (1531)
29. 深入浅出Liferay Portal (12) (1502)
30. 深入浅出Liferay Portal (3) (1472)
31. mvn功能简介 (1446)
32. Liferay Portal学习笔记之(五):开发主题风格theme(1431)
33. 使用Java实现CA(二)(1403)
34. Postgresql 不能远程连接(1396)
35. Liferay Portal学习笔记(三):自定义页面布局Template(1393)
36. liferay 部署(1382)
37. 深入浅出Liferay Portal (8) (1364)
38. 深入浅出Liferay Portal (2) (1361)
39. Problem with Sybase, PostgreSQL and Timestamp columns(1361)
40. 调用IE内部的打印控件来实现打印功能(1344)
评论排行榜
1. ClassNotFoundException: org.hibernate.hql.ast.HqlToken(8)
2. 测试-答对5道题的人是天才,答对4道的是帅才,答对3道的是将才,答对2道的是奇才,答对1道的是人才(5)
3. 用axis发布webservices(一) (3)
4. Liferay Portal学习笔记(一):安装(3)
5. JAVA中SSL证书认证通讯-Client(3)
6. Tomcat 5.5.2 下部署 Liferay 4.4.1(2)
7. 将JBoss启动做成Windows的系统服务(2)
8. 深入浅出Liferay Portal (12) (2)
9. 打印出Ibatis最终的SQL语句(2)
10. HashMap 、HashTable、HashSet的区别 (2)
11. java线程综述 (2)
12. Axis1.4 开发笔记(2)
13. 深入了解Java ClassLoader、Bytecode 、ASM、cglib (1)
14. 代理模式(1)
15. java对word、excel、pdf等操作综合文章(1)
16. Axis1.4 利用 deploy.wsdd 发布 server-config.wsdd文件(1)
17. 使用maven2 打ear包(1)
18. 深入浅出Liferay Portal (11) (1)
19. 深入浅出Liferay Portal (10) (1)
20. 转载 高负载系统架构设计(1)
21. 论坛灌水机 -- HTTPClient(1)
22. Apache Maven 2 简介(最全的文档)(1)
23. maven2完全使用手册(1)
24. 在Struts2.0中如何得到绝对路径(1)
25. Java深入:在Eclipse中如何利用Maven (0)
26. 教程--开始使用Maven下(0)
27. Maven2快速入门教程 (0)
28. 调用IE内部的打印控件来实现打印功能(0)
29. ibatis中文与like的问题 (0)
30. Chapter 1. Maven2(0)
31. Maven2 体验(0)
32. 1.1. Maven2 体验(0)
33. Maven2.0:编译、测试、部署、运行(0)
34. Maven2 常用命令(0)
35. Struts2.0 中配置 Struts-Menu(0)
36. maven2.0学习笔记 (0)
37. mvn功能简介 (0)
38. Maven中文手册 (0)
39. Maven使用手册(0)
40. maven翻译一结 (0)
JAVA解析纯真IP地址库
具体解析的纯真版IP地址库请详见
http://lumaqq.linuxsir.org/article/qqwry_format_detail.html
,这里就不多叙述了。
看下JAVA代码中怎么解析IP的吧。
(代码参考至lumaQQ.谢谢开源作者luma)
解析的主类
Java代码
package
com.showtime.IPparse;
import
java.io.File;
import
java.io.FileNotFoundException;
import
java.io.IOException;
import
java.io.RandomAccessFile;
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
java.util.Map;
import
com.showtime.util.LogFactory;
import
org.apache.log4j.Level;
public
class
IPSeeker {
//纯真IP数据库名
private
String IP_FILE=
"QQWry.Dat"
;
//保存的文件夹
private
String INSTALL_DIR=
"f:/qqwry"
;
// 一些固定常量,比如记录长度等等
private
static
final
int
IP_RECORD_LENGTH =
7
;
private
static
final
byte
REDIRECT_MODE_1 =
0x01
;
private
static
final
byte
REDIRECT_MODE_2 =
0x02
;
// 用来做为cache,查询一个ip时首先查看cache,以减少不必要的重复查找
private
Map<String, IPLocation> ipCache;
// 随机文件访问类
private
RandomAccessFile ipFile;
// 内存映射文件
private
MappedByteBuffer mbb;
// 起始地区的开始和结束的绝对偏移
private
long
ipBegin, ipEnd;
// 为提高效率而采用的临时变量
private
IPLocation loc;
private
byte
[] buf;
private
byte
[] b4;
private
byte
[] b3;
public
IPSeeker(String fileName,String dir) {
this
.INSTALL_DIR=dir;
this
.IP_FILE=fileName;
ipCache =
new
HashMap<String, IPLocation>();
loc =
new
IPLocation();
buf =
new
byte
[
100
];
b4 =
new
byte
[
4
];
b3 =
new
byte
[
3
];
try
{
ipFile =
new
RandomAccessFile(IP_FILE,
"r"
);
}
catch
(FileNotFoundException e) {
// 如果找不到这个文件,再尝试再当前目录下搜索,这次全部改用小写文件名
// 因为有些系统可能区分大小写导致找不到ip地址信息文件
String filename =
new
File(IP_FILE).getName().toLowerCase();
File[] files =
new
File(INSTALL_DIR).listFiles();
for
(
int
i =
0
; i < files.length; i++) {
if
(files[i].isFile()) {
if
(files[i].getName().toLowerCase().equals(filename)) {
try
{
ipFile =
new
RandomAccessFile(files[i],
"r"
);
}
catch
(FileNotFoundException e1) {
LogFactory.log(
"IP地址信息文件没有找到,IP显示功能将无法使用"
,Level.ERROR,e1);
ipFile =
null
;
}
break
;
}
}
}
}
// 如果打开文件成功,读取文件头信息
if
(ipFile !=
null
) {
try
{
ipBegin = readLong4(
0
);
ipEnd = readLong4(
4
);
if
(ipBegin == -
1
|| ipEnd == -
1
) {
ipFile.close();
ipFile =
null
;
}
}
catch
(IOException e) {
LogFactory.log(
"IP地址信息文件格式有错误,IP显示功能将无法使用"
,Level.ERROR,e);
ipFile =
null
;
}
}
}
/**
* 给定一个地点的不完全名字,得到一系列包含s子串的IP范围记录
* @param s 地点子串
* @return 包含IPEntry类型的List
*/
public
List getIPEntriesDebug(String s) {
List<IPEntry> ret =
new
ArrayList<IPEntry>();
long
endOffset = ipEnd +
4
;
for
(
long
offset = ipBegin +
4
; offset <= endOffset; offset += IP_RECORD_LENGTH) {
// 读取结束IP偏移
long
temp = readLong3(offset);
// 如果temp不等于-1,读取IP的地点信息
if
(temp != -
1
) {
IPLocation ipLoc = getIPLocation(temp);
// 判断是否这个地点里面包含了s子串,如果包含了,添加这个记录到List中,如果没有,继续
if
(ipLoc.getCountry().indexOf(s) != -
1
|| ipLoc.getArea().indexOf(s) != -
1
) {
IPEntry entry =
new
IPEntry();
entry.country = ipLoc.getCountry();
entry.area = ipLoc.getArea();
// 得到起始IP
readIP(offset -
4
, b4);
entry.beginIp = Util.getIpStringFromBytes(b4);
// 得到结束IP
readIP(temp, b4);
entry.endIp = Util.getIpStringFromBytes(b4);
// 添加该记录
ret.add(entry);
}
}
}
return
ret;
}
public
IPLocation getIPLocation(String ip){
IPLocation location=
new
IPLocation();
location.setArea(
this
.getArea(ip));
location.setCountry(
this
.getCountry(ip));
return
location;
}
/**
* 给定一个地点的不完全名字,得到一系列包含s子串的IP范围记录
* @param s 地点子串
* @return 包含IPEntry类型的List
*/
public
List<IPEntry> getIPEntries(String s) {
List<IPEntry> ret =
new
ArrayList<IPEntry>();
try
{
// 映射IP信息文件到内存中
if
(mbb ==
null
) {
FileChannel fc = ipFile.getChannel();
mbb = fc.map(FileChannel.MapMode.READ_ONLY,
0
, ipFile.length());
mbb.order(ByteOrder.LITTLE_ENDIAN);
}
int
endOffset = (
int
)ipEnd;
for
(
int
offset = (
int
)ipBegin +
4
; offset <= endOffset; offset += IP_RECORD_LENGTH) {
int
temp = readInt3(offset);
if
(temp != -
1
) {
IPLocation ipLoc = getIPLocation(temp);
// 判断是否这个地点里面包含了s子串,如果包含了,添加这个记录到List中,如果没有,继续
if
(ipLoc.getCountry().indexOf(s) != -
1
|| ipLoc.getArea().indexOf(s) != -
1
) {
IPEntry entry =
new
IPEntry();
entry.country = ipLoc.getCountry();
entry.area = ipLoc.getArea();
// 得到起始IP
readIP(offset -
4
, b4);
entry.beginIp = Util.getIpStringFromBytes(b4);
// 得到结束IP
readIP(temp, b4);
entry.endIp = Util.getIpStringFromBytes(b4);
// 添加该记录
ret.add(entry);
}
}
}
}
catch
(IOException e) {
LogFactory.log(
""
,Level.ERROR,e);
}
return
ret;
}
/**
* 从内存映射文件的offset位置开始的3个字节读取一个int
* @param offset
* @return
*/
private
int
readInt3(
int
offset) {
mbb.position(offset);
return
mbb.getInt() &
0x00FFFFFF
;
}
/**
* 从内存映射文件的当前位置开始的3个字节读取一个int
* @return
*/
private
int
readInt3() {
return
mbb.getInt() &
0x00FFFFFF
;
}
/**
* 根据IP得到国家名
* @param ip ip的字节数组形式
* @return 国家名字符串
*/
public
String getCountry(
byte
[] ip) {
// 检查ip地址文件是否正常
if
(ipFile ==
null
)
return
Message.bad_ip_file;
// 保存ip,转换ip字节数组为字符串形式
String ipStr = Util.getIpStringFromBytes(ip);
// 先检查cache中是否已经包含有这个ip的结果,没有再搜索文件
if
(ipCache.containsKey(ipStr)) {
IPLocation ipLoc = ipCache.get(ipStr);
return
ipLoc.getCountry();
}
else
{
IPLocation ipLoc = getIPLocation(ip);
ipCache.put(ipStr, ipLoc.getCopy());
return
ipLoc.getCountry();
}
}
/**
* 根据IP得到国家名
* @param ip IP的字符串形式
* @return 国家名字符串
*/
public
String getCountry(String ip) {
return
getCountry(Util.getIpByteArrayFromString(ip));
}
/**
* 根据IP得到地区名
* @param ip ip的字节数组形式
* @return 地区名字符串
*/
public
String getArea(
byte
[] ip) {
// 检查ip地址文件是否正常
if
(ipFile ==
null
)
return
Message.bad_ip_file;
// 保存ip,转换ip字节数组为字符串形式
String ipStr = Util.getIpStringFromBytes(ip);
// 先检查cache中是否已经包含有这个ip的结果,没有再搜索文件
if
(ipCache.containsKey(ipStr)) {
IPLocation ipLoc = ipCache.get(ipStr);
return
ipLoc.getArea();
}
else
{
IPLocation ipLoc = getIPLocation(ip);
ipCache.put(ipStr, ipLoc.getCopy());
return
ipLoc.getArea();
}
}
/**
* 根据IP得到地区名
* @param ip IP的字符串形式
* @return 地区名字符串
*/
public
String getArea(String ip) {
return
getArea(Util.getIpByteArrayFromString(ip));
}
/**
* 根据ip搜索ip信息文件,得到IPLocation结构,所搜索的ip参数从类成员ip中得到
* @param ip 要查询的IP
* @return IPLocation结构
*/
private
IPLocation getIPLocation(
byte
[] ip) {
IPLocation info =
null
;
long
offset = locateIP(ip);
if
(offset != -
1
)
info = getIPLocation(offset);
if
(info ==
null
) {
info =
new
IPLocation();
info.setCountry ( Message.unknown_country);
info.setArea(Message.unknown_area);
}
return
info;
}
/**
* 从offset位置读取4个字节为一个long,因为java为big-endian格式,所以没办法
* 用了这么一个函数来做转换
* @param offset
* @return 读取的long值,返回-1表示读取文件失败
*/
private
long
readLong4(
long
offset) {
long
ret =
0
;
try
{
ipFile.seek(offset);
ret |= (ipFile.readByte() &
0xFF
);
ret |= ((ipFile.readByte() <<
8
) &
0xFF00
);
ret |= ((ipFile.readByte() <<
16
) &
0xFF0000
);
ret |= ((ipFile.readByte() <<
24
) &
0xFF000000
);
return
ret;
}
catch
(IOException e) {
return
-
1
;
}
}
/**
* 从offset位置读取3个字节为一个long,因为java为big-endian格式,所以没办法
* 用了这么一个函数来做转换
* @param offset 整数的起始偏移
* @return 读取的long值,返回-1表示读取文件失败
*/
private
long
readLong3(
long
offset) {
long
ret =
0
;
try
{
ipFile.seek(offset);
ipFile.readFully(b3);
ret |= (b3[
0
] &
0xFF
);
ret |= ((b3[
1
] <<
8
) &
0xFF00
);
ret |= ((b3[
2
] <<
16
) &
0xFF0000
);
return
ret;
}
catch
(IOException e) {
return
-
1
;
}
}
/**
* 从当前位置读取3个字节转换成long
* @return 读取的long值,返回-1表示读取文件失败
*/
private
long
readLong3() {
long
ret =
0
;
try
{
ipFile.readFully(b3);
ret |= (b3[
0
] &
0xFF
);
ret |= ((b3[
1
] <<
8
) &
0xFF00
);
ret |= ((b3[
2
] <<
16
) &
0xFF0000
);
return
ret;
}
catch
(IOException e) {
return
-
1
;
}
}
/**
* 从offset位置读取四个字节的ip地址放入ip数组中,读取后的ip为big-endian格式,但是
* 文件中是little-endian形式,将会进行转换
* @param offset
* @param ip
*/
private
void
readIP(
long
offset,
byte
[] ip) {
try
{
ipFile.seek(offset);
ipFile.readFully(ip);
byte
temp = ip[
0
];
ip[
0
] = ip[
3
];
ip[
3
] = temp;
temp = ip[
1
];
ip[
1
] = ip[
2
];
ip[
2
] = temp;
}
catch
(IOException e) {
LogFactory.log(
""
,Level.ERROR,e);
}
}
/**
* 从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
long
locateIP(
byte
[] ip) {
long
m =
0
;
int
r;
// 比较第一个ip项
readIP(ipBegin, b4);
r = compareIP(ip, b4);
if
(r ==
0
)
return
ipBegin;
else
if
(r <
0
)
return
-
1
;
// 开始二分搜索
for
(
long
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
readLong3(m +
4
);
}
// 如果循环结束了,那么i和j必定是相等的,这个记录为最可能的记录,但是并非
// 肯定就是,还要检查一下,如果是,就返回结束地址区的绝对偏移
m = readLong3(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
long
getMiddleOffset(
long
begin,
long
end) {
long
records = (end - begin) / IP_RECORD_LENGTH;
records >>=
1
;
if
(records ==
0
) records =
1
;
return
begin + records * IP_RECORD_LENGTH;
}
/**
* 给定一个ip国家地区记录的偏移,返回一个IPLocation结构
* @param offset 国家记录的起始偏移
* @return IPLocation对象
*/
private
IPLocation getIPLocation(
long
offset) {
try
{
// 跳过4字节ip
ipFile.seek(offset +
4
);
// 读取第一个字节判断是否标志字节
byte
b = ipFile.readByte();
if
(b == REDIRECT_MODE_1) {
// 读取国家偏移
long
countryOffset = readLong3();
// 跳转至偏移处
ipFile.seek(countryOffset);
// 再检查一次标志字节,因为这个时候这个地方仍然可能是个重定向
b = ipFile.readByte();
if
(b == REDIRECT_MODE_2) {
loc.setCountry ( readString(readLong3()));
ipFile.seek(countryOffset +
4
);
}
else
loc.setCountry ( readString(countryOffset));
// 读取地区标志
loc.setArea( readArea(ipFile.getFilePointer()));
}
else
if
(b == REDIRECT_MODE_2) {
loc.setCountry ( readString(readLong3()));
loc.setArea( readArea(offset +
8
));
}
else
{
loc.setCountry ( readString(ipFile.getFilePointer() -
1
));
loc.setArea( readArea(ipFile.getFilePointer()));
}
return
loc;
}
catch
(IOException e) {
return
null
;
}
}
/**
* 给定一个ip国家地区记录的偏移,返回一个IPLocation结构,此方法应用与内存映射文件方式
* @param offset 国家记录的起始偏移
* @return IPLocation对象
*/
private
IPLocation getIPLocation(
int
offset) {
// 跳过4字节ip
mbb.position(offset +
4
);
// 读取第一个字节判断是否标志字节
byte
b = mbb.get();
if
(b == REDIRECT_MODE_1) {
// 读取国家偏移
int
countryOffset = readInt3();
// 跳转至偏移处
mbb.position(countryOffset);
// 再检查一次标志字节,因为这个时候这个地方仍然可能是个重定向
b = mbb.get();
if
(b == REDIRECT_MODE_2) {
loc.setCountry ( readString(readInt3()));
mbb.position(countryOffset +
4
);
}
else
loc.setCountry ( readString(countryOffset));
// 读取地区标志
loc.setArea(readArea(mbb.position()));
}
else
if
(b == REDIRECT_MODE_2) {
loc.setCountry ( readString(readInt3()));
loc.setArea(readArea(offset +
8
));
}
else
{
loc.setCountry ( readString(mbb.position() -
1
));
loc.setArea(readArea(mbb.position()));
}
return
loc;
}
/**
* 从offset偏移开始解析后面的字节,读出一个地区名
* @param offset 地区记录的起始偏移
* @return 地区名字符串
* @throws IOException
*/
private
String readArea(
long
offset)
throws
IOException {
ipFile.seek(offset);
byte
b = ipFile.readByte();
if
(b == REDIRECT_MODE_1 || b == REDIRECT_MODE_2) {
long
areaOffset = readLong3(offset +
1
);
if
(areaOffset ==
0
)
return
Message.unknown_area;
else
return
readString(areaOffset);
}
else
return
readString(offset);
}
/**
* @param offset 地区记录的起始偏移
* @return 地区名字符串
*/
private
String readArea(
int
offset) {
mbb.position(offset);
byte
b = mbb.get();
if
(b == REDIRECT_MODE_1 || b == REDIRECT_MODE_2) {
int
areaOffset = readInt3();
if
(areaOffset ==
0
)
return
Message.unknown_area;
else
return
readString(areaOffset);
}
else
return
readString(offset);
}
/**
* 从offset偏移处读取一个以0结束的字符串
* @param offset 字符串起始偏移
* @return 读取的字符串,出错返回空字符串
*/
private
String readString(
long
offset) {
try
{
ipFile.seek(offset);
int
i;
for
(i =
0
, buf[i] = ipFile.readByte(); buf[i] !=
0
; buf[++i] = ipFile.readByte());
if
(i !=
0
)
return
Util.getString(buf,
0
, i,
"GBK"
);
}
catch
(IOException e) {
LogFactory.log(
""
,Level.ERROR,e);
}
return
""
;
}
/**
* 从内存映射文件的offset位置得到一个0结尾字符串
* @param offset 字符串起始偏移
* @return 读取的字符串,出错返回空字符串
*/
private
String readString(
int
offset) {
try
{
mbb.position(offset);
int
i;
for
(i =
0
, buf[i] = mbb.get(); buf[i] !=
0
; buf[++i] = mbb.get());
if
(i !=
0
)
return
Util.getString(buf,
0
, i,
"GBK"
);
}
catch
(IllegalArgumentException e) {
LogFactory.log(
""
,Level.ERROR,e);
}
return
""
;
}
}
在实际项目用我使用spring注入IP地址库文件的名字和所在目录,并能保证IPSeeker的单一实例。
下面是个工具类,把string和btye数组之间互相转换的类。
Java代码
package
com.showtime.IPparse;
import
java.io.UnsupportedEncodingException;
import
java.util.StringTokenizer;
import
org.apache.log4j.Level;
import
com.showtime.util.LogFactory;
/**
* 工具类,提供一些方便的方法
*/
public
class
Util {
private
static
StringBuilder sb =
new
StringBuilder();
/**
* 从ip的字符串形式得到字节数组形式
* @param ip 字符串形式的ip
* @return 字节数组形式的ip
*/
public
static
byte
[] getIpByteArrayFromString(String ip) {
byte
[] ret =
new
byte
[
4
];
StringTokenizer st =
new
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) {
LogFactory.log(
"从ip的字符串形式得到字节数组形式报错"
, Level.ERROR, e);
}
return
ret;
}
/**
* @param ip ip的字节数组形式
* @return 字符串形式的ip
*/
public
static
String getIpStringFromBytes(
byte
[] ip) {
sb.delete(
0
, sb.length());
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();
}
/**
* 根据某种编码方式将字节数组转换成字符串
* @param b 字节数组
* @param offset 要转换的起始位置
* @param len 要转换的长度
* @param encoding 编码方式
* @return 如果encoding不支持,返回一个缺省编码的字符串
*/
public
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);
}
}
}
下面是个常量值的类,用接口形式来定义省事不少。
Java代码
package
com.showtime.IPparse;
public
interface
Message {
String bad_ip_file=
"IP地址库文件错误"
;
String unknown_country=
"未知国家"
;
String unknown_area=
"未知地区"
;
}
一个封装国家和地区的实体类
Java代码
package
com.showtime.IPparse;
/**
*
* @category 用来封装ip相关信息,目前只有两个字段,ip所在的国家和地区
*/
public
class
IPLocation {
private
String country;
private
String area;
public
IPLocation() {
country = area =
""
;
}
public
IPLocation getCopy() {
IPLocation ret =
new
IPLocation();
ret.country = country;
ret.area = area;
return
ret;
}
public
String getCountry() {
return
country;
}
public
void
setCountry(String country) {
this
.country = country;
}
public
String getArea() {
return
area;
}
public
void
setArea(String area) {
//如果为局域网,纯真IP地址库的地区会显示CZ88.NET,这里把它去掉
if
(area.trim().equals(
"CZ88.NET"
)){
this
.area=
"本机或本网络"
;
}
else
{
this
.area = area;
}
}
}
一下是一个范围记录的类
Java代码
package
com.showtime.IPparse;
/**
* <pre>
* 一条IP范围记录,不仅包括国家和区域,也包括起始IP和结束IP
* </pre>
*/
public
class
IPEntry {
public
String beginIp;
public
String endIp;
public
String country;
public
String area;
/**
* 构造函数
*/
public
IPEntry() {
beginIp = endIp = country = area =
""
;
}
}
日志记录类
Java代码
package
com.showtime.util;
import
org.apache.log4j.Level;
import
org.apache.log4j.Logger;
/**
*
*
* 日志工厂
*/
public
class
LogFactory {
private
static
final
Logger logger;
static
{
logger = Logger.getLogger(
"stdout"
);
logger.setLevel(Level.DEBUG);
}
public
static
void
log(String info, Level level, Throwable ex) {
logger.log(level, info, ex);
}
public
static
Level getLogLevel(){
return
logger.getLevel();
}
}
下面是测试类
Java代码
package
com.showtime.IPparse;
import
junit.framework.TestCase;
public
class
IPtest
extends
TestCase {
public
void
testIp(){
//指定纯真数据库的文件名,所在文件夹
IPSeeker ip=
new
IPSeeker(
"QQWry.Dat"
,
"f:/qqwry"
);
//测试IP 58.20.43.13
System.out.println(ip.getIPLocation(
"58.20.43.13"
).getCountry()+
":"
+ip.getIPLocation(
"58.20.43.13"
).getArea());
}
}
当输出:湖南省长沙市:网通
posted on 2010-11-17 22:28
礼物
阅读(5712)
评论(1)
编辑
收藏
评论
#
re: JAVA解析纯真IP地址库
2014-04-27 12:42
苏秦
你这个有多线程并发问题
回复
更多评论
新用户注册
刷新评论列表
只有注册用户
登录
后才能发表评论。
网站导航:
博客园
IT新闻
Chat2DB
C++博客
博问
管理
Powered by:
BlogJava
Copyright © 礼物