生活随笔
收集整理的這篇文章主要介紹了
纯真IP库的结构分析及一个查询类
小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
?? ?個人網(wǎng)站上有個功能,記錄訪問者的IP及其歸屬地。最初我偷懶通過一個WebService來查詢IP歸屬地,后來覺得通過這種方法響應(yīng)時間長,資源耗費大,而且對那個WebSerice的依賴度太高,如果它掛了或者網(wǎng)絡(luò)原因,經(jīng)常要到超時才返回。所以,我打算直接從本地的純真IP庫里查詢。
?? ?純真庫的數(shù)據(jù)結(jié)構(gòu)在http://lumaqq.linuxsir.org/article/qqwry_format_detail.html上講的很詳細(xì)了。簡單地講數(shù)據(jù)文件分成三個區(qū)域:
?? ?1、文件頭(8個字節(jié),前4字節(jié)是指向索引區(qū)第一條記錄,后4字節(jié)指向索引區(qū)最后一條記錄)
?? ?2、記錄區(qū)(一個記錄包含IP地址,國家記錄,地區(qū)記錄,后兩個記錄有可能是字符串,也可能是重定向,有多種重定向模式)
?? ?3、索引區(qū)(一個索引定長7個字節(jié),前4字節(jié)是IP地址(little-endian),后3字節(jié)指向?qū)?yīng)記錄區(qū)的位置,這里的位置指從文件頭開始計算的偏移字節(jié))
?? 雖然這個庫結(jié)構(gòu)工作的很好,效率也沒有問題,但是我覺得設(shè)計的有點小復(fù)雜了。而且,如果記錄區(qū)中有條記錄A,是重定向到記錄B中的,假如我刪除了記錄B,查詢記錄A的時候就會有問題。當(dāng)然,可以在刪除記錄B的時候進行相應(yīng)處理,只是有些麻煩。如果把文件結(jié)構(gòu)改成如下,應(yīng)該處理起來會更方便一些:
?? 1、文件頭(與原庫一樣)
?? 2、字符串區(qū)
?? 3、索引區(qū)(4字節(jié)的IP地址,4字節(jié)的偏移值,4字節(jié)的偏移值)
所有字符串放在字符串區(qū)中,統(tǒng)一管理。索引區(qū)中放IP地址,國家記錄的“指針”和區(qū)域記錄的“指針”,所謂的“指針”是對應(yīng)到字符串區(qū)中某條的字符串偏移值。
?
不過既然純真IP庫是這么設(shè)計的,我只好根據(jù)它的結(jié)構(gòu)來進行相應(yīng)的查詢。
索引區(qū)的記錄是從小到大排列的,可以用二分法來查詢。
IP庫中索引的IP地址,并不是連續(xù)的,舉個例子,192.168.0.0的后一條記錄并不是192.168.0.1,可能是192.169.0.0,也就是說,它存儲的一個是IP段。所以要做一個類似于“四舍五入”的處理。好在大部分情況下,我們都只要舍掉就可以了,比如查詢192.168.1.1應(yīng)該匹配192.168.0.0而不是192.169.0.0。
?
import?java.io.*;??public?class?IPSeeker?{?????protected?RandomAccessFile?ipDataFile;?????protected?final?int?RECORD_LEN?=?7;?????protected?final?int?MODE_1?=?0x01;??????protected?final?int?MODE_2?=?0x02;??????protected?final?int?MODE_3?=?0x03;??????protected?long?indexBegin;?????protected?long?indexEnd;??????????????public?IPSeeker()?throws?Exception?????{??????????????????ipDataFile?=?new?RandomAccessFile("qqwry.dat",?"r");??????????????????indexBegin?=?readLong(4,?0);?????????????????????indexEnd?=?readLong(4,?4);?????}??????????public?static?void?main(String[]?args)?throws?Exception?????{?????????IPSeeker?seeker?=?new?IPSeeker();?????????String?result?=?seeker.search("111.2.13.4");?????????System.out.println(result);?????????seeker.close();?????????seeker?=?null;?????}???????????????@Override?????protected?void?finalize()?throws?Throwable?????{?????????try?????????{?????????????ipDataFile.close();?????????}??????????catch?(IOException?e)?????????{??????????????????????}?????????super.finalize();?????}???????????????public?void?close()?????{?????????try?????????{?????????????ipDataFile.close();?????????}??????????catch?(IOException?e)?????????{??????????????????????}?????}??????public?String?search(String?ipStr)?throws?Exception?????{??????????????????long?recordCount?=?(indexEnd?-?indexBegin)?/?7?+?1;??????????????????long?itemStart?=?0;?????????long?itemEnd?=?recordCount?-?1;?????????long?ip?=?IPSeeker.stringIP2Long(ipStr);?????????long?middle?=?0;?????????long?midIP?=?0;?????????while(itemStart?<=?itemEnd)?????????{?????????????middle?=?(itemStart?+??itemEnd)?/?2;?????????????midIP?=?readLong(4,?indexBegin?+?middle?*?7);??????????????????????????if(midIP?==?ip)?????????????{?????????????????break;?????????????}?????????????else?if(midIP?<?ip)?????????????{?????????????????itemStart?=?middle?+?1;?????????????}?????????????else?????????????{?????????????????itemEnd?=?middle?-?1;?????????????}?????????}???????????????????????????if(ip?<?midIP?&&?middle?>?0)?????????{?????????????middle--;?????????}??????????long?item?=?readLong(3,?indexBegin?+?middle?*?7?+?4);?????????String[]?result?=?getInfo(item?+?4);?????????return?long2StringIP(readLong(4,?indexBegin?+?middle?*?7))+?","?????????????????+?result[0]?+?","??????????????????+?result[1];?????}???????????????public?static?String?long2StringIP(long?ip)?????{?????????long?ip4?=?ip?>>?0?&?0x000000FF;?????????long?ip3?=?ip?>>?8?&?0x000000FF;?????????long?ip2?=?ip?>>?16?&?0x000000FF;?????????long?ip1?=?ip?>>?24?&?0x000000FF;??????????????????return?String.valueOf(ip1)?+?"."?+?String.valueOf(ip2)?+?"."?+??????????????????String.valueOf(ip3)?+?"."?+?String.valueOf(ip4);?????}???????????????public?static?Long?stringIP2Long(String?ipStr)?throws?Exception?????{?????????String[]?list?=?ipStr.split("\\.");?????????if(list.length?!=?4)?????????{?????????????throw?new?Exception("IP地址格式錯誤");?????????}?????????long?ip?=?Long.parseLong(list[0])?<<?24?&?0xFF000000;?????????ip?+=?Long.parseLong(list[1])?<<?16?&?0x00FF0000;?????????ip?+=?Long.parseLong(list[2])?<<?8?&?0x0000FF00;?????????ip?+=?Long.parseLong(list[3])?<<?0?&?0x000000FF;?????????return?ip;?????}???????????????private?long?readLong(int?nByte,?long?offset)?throws?Exception?????{?????????ipDataFile.seek(offset);??????????????????long?result?=?0;?????????if(nByte?>?4?||?nByte?<?0)?????????????throw?new?Exception("nBit?should?be?0-4");??????????????????for(int?i?=?0;?i?<?nByte;?i++)?????????{?????????????result?|=?((long)ipDataFile.readByte()?<<?8?*?i)?&?(0xFFL?<<?8?*?i);?????????}??????????????????return?result;?????}??????????private?String[]?getInfo(long?itemStartPos)?throws?Exception?????{??????????????????String[]?result?=?new?String[2];??????????????????ipDataFile.seek(itemStartPos);?????????int?mode?=?(int)ipDataFile.readByte();?????????switch?(mode)?????????{?????????case?MODE_1:?????????{?????????????long?offset?=?itemStartPos?+?1;?????????????long?redirPos?=?readLong(3,?offset);?????????????result?=?getInfo(redirPos);?????????}?????????break;?????????case?MODE_2:?????????{?????????????long?offset?=?itemStartPos?+?1;?????????????long?redirPos?=?readLong(3,?offset);?????????????result?=?getInfo(redirPos);?????????????result[1]?=?getArea(offset?+?3);?????????}?????????break;?????????default:?????????{?????????????long?offset?=?itemStartPos;?????????????int?countryLen?=?getStrLength(offset);?????????????result[0]?=?getString(offset,?countryLen);??????????????????????????offset?=?itemStartPos?+?countryLen?+?1;?????????????result[1]?=?getArea(offset);?????????}?????????break;?????????}?????????return?result;?????}??????????????private?String?getArea(long?offset)?throws?Exception?????{?????????ipDataFile.seek(offset);?????????int?cityMode?=?(int)ipDataFile.readByte();?????????if(cityMode?==??MODE_2?||?cityMode?==??MODE_1)?????????{?????????????offset?=?readLong(3,?offset?+?1);?????????}??????????????????int?cityLen?=?getStrLength(offset);??????????return?getString(offset,?cityLen);???????}??????????private?int?getStrLength(long?pos)?throws?IOException?????{?????????ipDataFile.seek(pos);?????????long?strEnd?=?pos?-?1;?????????while(ipDataFile.readByte()?!=?(byte)0)?????????{?????????????strEnd++;?????????}??????????????????return?(int)?(strEnd?-?pos?+?1);?????}??????????private?String?getString(long?pos,?int?len)?throws?IOException?????{?????????byte?buf[]?=?new?byte[len];??????????????????ipDataFile.seek(pos);?????????ipDataFile.read(buf);?????????String?s?=?new?String(buf,?"gbk");?????????return?s;?????}??}? ?
?
?
轉(zhuǎn)載于:https://blog.51cto.com/jianshusoft/624327
總結(jié)
以上是生活随笔為你收集整理的纯真IP库的结构分析及一个查询类的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。