纯真ip java_IP 3 JAVA解析纯真IP地址库

packagecom.showtime.IPparse;importjava.io.File;importjava.io.FileNotFoundException;importjava.io.IOException;importjava.io.RandomAccessFile;importjava.nio.ByteOrder;importjava.nio.MappedByteBuffer;importjava.nio.channels.FileChannel;importjava.util.ArrayList;importjava.util.HashMap;importjava.util.List;importjava.util.Map;importcom.showtime.util.LogFactory;importorg.apache.log4j.Level;public classIPSeeker {//纯真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 MapipCache;//随机文件访问类

privateRandomAccessFile ipFile;//内存映射文件

privateMappedByteBuffer mbb;//起始地区的开始和结束的绝对偏移

private longipBegin, ipEnd;//为提高效率而采用的临时变量

privateIPLocation loc;private byte[] buf;private byte[] b4;private byte[] b3;publicIPSeeker(String fileName,String dir) {this.INSTALL_DIR=dir;this.IP_FILE=fileName;

ipCache= new HashMap();

loc= newIPLocation();

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 = newFile(IP_FILE).getName().toLowerCase();

File[] files= newFile(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范围记录

*@params 地点子串

*@return包含IPEntry类型的List*/

publicList getIPEntriesDebug(String s) {

List ret = new ArrayList();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= newIPEntry();

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

}

}

}returnret;

}publicIPLocation getIPLocation(String ip){

IPLocation location=newIPLocation();

location.setArea(this.getArea(ip));

location.setCountry(this.getCountry(ip));returnlocation;

}/*** 给定一个地点的不完全名字,得到一系列包含s子串的IP范围记录

*@params 地点子串

*@return包含IPEntry类型的List*/

public ListgetIPEntries(String s) {

List ret = new ArrayList();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= newIPEntry();

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

}returnret;

}/*** 从内存映射文件的offset位置开始的3个字节读取一个int

*@paramoffset

*@return

*/

private int readInt3(intoffset) {

mbb.position(offset);return mbb.getInt() & 0x00FFFFFF;

}/*** 从内存映射文件的当前位置开始的3个字节读取一个int

*@return

*/

private intreadInt3() {return mbb.getInt() & 0x00FFFFFF;

}/*** 根据IP得到国家名

*@paramip ip的字节数组形式

*@return国家名字符串*/

public String getCountry(byte[] ip) {//检查ip地址文件是否正常

if(ipFile == null)returnMessage.bad_ip_file;//保存ip,转换ip字节数组为字符串形式

String ipStr =Util.getIpStringFromBytes(ip);//先检查cache中是否已经包含有这个ip的结果,没有再搜索文件

if(ipCache.containsKey(ipStr)) {

IPLocation ipLoc=ipCache.get(ipStr);returnipLoc.getCountry();

}else{

IPLocation ipLoc=getIPLocation(ip);

ipCache.put(ipStr, ipLoc.getCopy());returnipLoc.getCountry();

}

}/*** 根据IP得到国家名

*@paramip IP的字符串形式

*@return国家名字符串*/

publicString getCountry(String ip) {returngetCountry(Util.getIpByteArrayFromString(ip));

}/*** 根据IP得到地区名

*@paramip ip的字节数组形式

*@return地区名字符串*/

public String getArea(byte[] ip) {//检查ip地址文件是否正常

if(ipFile == null)returnMessage.bad_ip_file;//保存ip,转换ip字节数组为字符串形式

String ipStr =Util.getIpStringFromBytes(ip);//先检查cache中是否已经包含有这个ip的结果,没有再搜索文件

if(ipCache.containsKey(ipStr)) {

IPLocation ipLoc=ipCache.get(ipStr);returnipLoc.getArea();

}else{

IPLocation ipLoc=getIPLocation(ip);

ipCache.put(ipStr, ipLoc.getCopy());returnipLoc.getArea();

}

}/*** 根据IP得到地区名

*@paramip IP的字符串形式

*@return地区名字符串*/

publicString getArea(String ip) {returngetArea(Util.getIpByteArrayFromString(ip));

}/*** 根据ip搜索ip信息文件,得到IPLocation结构,所搜索的ip参数从类成员ip中得到

*@paramip 要查询的IP

*@returnIPLocation结构*/

private IPLocation getIPLocation(byte[] ip) {

IPLocation info= null;long offset =locateIP(ip);if(offset != -1)

info=getIPLocation(offset);if(info == null) {

info= newIPLocation();

info.setCountry ( Message.unknown_country);

info.setArea(Message.unknown_area);

}returninfo;

}/*** 从offset位置读取4个字节为一个long,因为java为big-endian格式,所以没办法

* 用了这么一个函数来做转换

*@paramoffset

*@return读取的long值,返回-1表示读取文件失败*/

private long readLong4(longoffset) {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);returnret;

}catch(IOException e) {return -1;

}

}/*** 从offset位置读取3个字节为一个long,因为java为big-endian格式,所以没办法

* 用了这么一个函数来做转换

*@paramoffset 整数的起始偏移

*@return读取的long值,返回-1表示读取文件失败*/

private long readLong3(longoffset) {long ret = 0;try{

ipFile.seek(offset);

ipFile.readFully(b3);

ret|= (b3[0] & 0xFF);

ret|= ((b3[1] << 8) & 0xFF00);

ret|= ((b3[2] << 16) & 0xFF0000);returnret;

}catch(IOException e) {return -1;

}

}/*** 从当前位置读取3个字节转换成long

*@return读取的long值,返回-1表示读取文件失败*/

private longreadLong3() {long ret = 0;try{

ipFile.readFully(b3);

ret|= (b3[0] & 0xFF);

ret|= ((b3[1] << 8) & 0xFF00);

ret|= ((b3[2] << 16) & 0xFF0000);returnret;

}catch(IOException e) {return -1;

}

}/*** 从offset位置读取四个字节的ip地址放入ip数组中,读取后的ip为big-endian格式,但是

* 文件中是little-endian形式,将会进行转换

*@paramoffset

*@paramip*/

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形式,将会进行转换

*@paramoffset

*@paramip*/

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的

*@paramip 要查询的IP

*@parambeginIp 和被查询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)returnr;

}return 0;

}/*** 把两个byte当作无符号数进行比较

*@paramb1

*@paramb2

*@return若b1大于b2则返回1,相等返回0,小于返回-1*/

private int compareByte(byte b1, byteb2) {if((b1 & 0xFF) > (b2 & 0xFF)) //比较是否大于

return 1;else if((b1 ^ b2) == 0)//判断是否相等

return 0;else

return -1;

}/*** 这个方法将根据ip的内容,定位到包含这个ip国家地区的记录处,返回一个绝对偏移

* 方法使用二分法查找。

*@paramip 要查询的IP

*@return如果找到了,返回结束IP的偏移,如果没有找到,返回-1*/

private long locateIP(byte[] ip) {long m = 0;intr;//比较第一个ip项

readIP(ipBegin, b4);

r=compareIP(ip, b4);if(r == 0) returnipBegin;else if(r < 0) return -1;//开始二分搜索

for(long i = ipBegin, j = ipEnd; i

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;

}elsej=m;

}else

return readLong3(m + 4);

}//如果循环结束了,那么i和j必定是相等的,这个记录为最可能的记录,但是并非//肯定就是,还要检查一下,如果是,就返回结束地址区的绝对偏移

m = readLong3(m + 4);

readIP(m, b4);

r=compareIP(ip, b4);if(r <= 0) returnm;else return -1;

}/*** 得到begin偏移和end偏移中间位置记录的偏移

*@parambegin

*@paramend

*@return

*/

private long getMiddleOffset(long begin, longend) {long records = (end - begin) /IP_RECORD_LENGTH;

records>>= 1;if(records == 0) records = 1;return begin + records *IP_RECORD_LENGTH;

}/*** 给定一个ip国家地区记录的偏移,返回一个IPLocation结构

*@paramoffset 国家记录的起始偏移

*@returnIPLocation对象*/

private IPLocation getIPLocation(longoffset) {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);

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

}returnloc;

}catch(IOException e) {return null;

}

}/*** 给定一个ip国家地区记录的偏移,返回一个IPLocation结构,此方法应用与内存映射文件方式

*@paramoffset 国家记录的起始偏移

*@returnIPLocation对象*/

private IPLocation getIPLocation(intoffset) {//跳过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);

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

}returnloc;

}/*** 从offset偏移开始解析后面的字节,读出一个地区名

*@paramoffset 地区记录的起始偏移

*@return地区名字符串

*@throwsIOException*/

private String readArea(long offset) throwsIOException {

ipFile.seek(offset);byte b =ipFile.readByte();if(b == REDIRECT_MODE_1 || b ==REDIRECT_MODE_2) {long areaOffset = readLong3(offset + 1);if(areaOffset == 0)returnMessage.unknown_area;else

returnreadString(areaOffset);

}else

returnreadString(offset);

}/***@paramoffset 地区记录的起始偏移

*@return地区名字符串*/

private String readArea(intoffset) {

mbb.position(offset);byte b =mbb.get();if(b == REDIRECT_MODE_1 || b ==REDIRECT_MODE_2) {int areaOffset =readInt3();if(areaOffset == 0)returnMessage.unknown_area;else

returnreadString(areaOffset);

}else

returnreadString(offset);

}/*** 从offset偏移处读取一个以0结束的字符串

*@paramoffset 字符串起始偏移

*@return读取的字符串,出错返回空字符串*/

private String readString(longoffset) {try{

ipFile.seek(offset);inti;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结尾字符串

*@paramoffset 字符串起始偏移

*@return读取的字符串,出错返回空字符串*/

private String readString(intoffset) {try{

mbb.position(offset);inti;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 "";

}

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值