锁定老帖子 主题:IPParse: IP 地址查询
该帖已经被评为良好帖
|
|
---|---|
作者 | 正文 |
发表时间:2009-02-19
QuakeWang 写道 看了一下代码,简单易懂,和纯真的ip数据库查找算法比起来,都是采用2分法查找ip范围,不同的是你这个用空间换时间,地址信息是重复记录的,速度自然要快很多。
在github上给你pull request,一个简单的性能改进: def self.format(ip) ip.to_s.scan(/\d+/).map{|x| x.rjust(3, '0')}.join('.') end 另外你可以试试看将这里的代码分开2次写,2个元素的数组用each感觉没有必要,而且block也会有少许的性能损失。 [ip[0,3].to_i,0].each do |f| #.... end 谢谢,已合并,性能又提升了0.*秒 ![]() 分开写没有感觉到性能的提升,并且代码量也会多一些 另外你提供使用Benchmark做性能测试结果会存在偏差,因为里面包含ips.each的时间,10万次差不多也要花 0.0* 秒 ![]() |
|
返回顶楼 | |
发表时间:2009-02-20
wosmvp 写道 确定找不到?
require 'rubygems' require 'ipparse' puts IPParse.parse('222.222.222.222') #=> 河北省石家庄市 另外代码你没有测试吧,试了几个全UNKNOW self.dichotomizing(arg,ip) 方法中的split影响性能,所以换了 性能测试有点问题,这样测试内容将包含randip这个方法的速度了 谢谢,文件竟然没有关闭…… ![]() 我从你发帖时写的那个地址下的data文件,不知道你后来改了没。0.txt里本身就有很多Unknown,不过大小写不一样。而且每个文件开头都有一段空白,split会出问题,我删掉了。我也没测试,刚把randip分析的结果输出到文件里看了下,是有几个没找到。好像每个文件第一条记录都没有分析出来,我再看看。那个benchmark我就随便写着看看时间,无所谓那么精确了。profile了下,主要都是IO#each消耗的时间。如果没有那个data缓存,估计会慢很多。 |
|
返回顶楼 | |
发表时间:2009-02-20
最后修改:2009-02-20
刚用netbeans打开data文件看了下,发现之前删文件开头空白的时候,完了还多出来一个.,不知道怎么搞的。现在好像没问题了。我用1000个随机ip看了下,没发现大些的UNKNOWN。用的WIN XP+RUBY1.9.1,data文件和结果在附件里,不过效率确实低了。
[size=xx-small] user system total real 13.969000 0.500000 14.469000 ( 14.515625) % cumulative self self total time seconds seconds calls ms/call ms/call name 20.77 2.90 2.90 33 88.03 88.03 IO#each 5.72 3.71 0.80 18071 0.04 0.06 BasicObject#!= 4.88 4.39 0.68 45503 0.01 0.01 Array#[] 4.81 5.06 0.67 2000 0.34 0.46 Array#each 3.48 5.55 0.49 13366 0.04 0.04 String#split 3.11 5.98 0.43 20071 0.02 0.02 Fixnum#== 2.88 6.38 0.40 11358 0.04 0.05 Comparable.> 2.78 6.77 0.39 6697 0.06 0.07 Comparable.< 2.45 7.12 0.34 992 0.35 0.77 IPParse#dichotomizing 2.12 7.41 0.30 18071 0.02 0.02 String#<=> 1.78 7.66 0.25 1000 0.25 0.88 IPParse#format 1.44 7.86 0.20 11366 0.02 0.02 Fixnum#/ 1.24 8.04 0.17 2000 0.09 0.12 Array#join 1.12 8.19 0.16 11366 0.01 0.01 Array#size 0.89 8.32 0.12 2001 0.06 0.09 Integer#times 0.87 8.44 0.12 5850 0.02 0.02 Fixnum#to_s 0.68 8.53 0.10 5000 0.02 0.02 String#to_i 0.66 8.63 0.09 1000 0.09 0.58 Object#randip 0.56 8.70 0.08 2000 0.04 0.38 Enumerable.inject 0.55 8.78 0.08 1000 0.08 0.08 IO#<< 0.44 8.84 0.06 4000 0.02 0.02 Array#<< 0.34 8.89 0.05 4000 0.01 0.01 String#rjust 0.34 8.94 0.05 4000 0.01 0.01 Kernel.rand 0.34 8.99 0.05 3000 0.02 0.02 String#to_s 0.23 9.02 0.03 4000 0.01 0.01 Fixnum#> 0.22 9.05 0.03 1000 0.03 0.17 Enumerator#each 0.21 9.08 0.03 1000 0.03 0.03 String#=~ 0.11 9.10 0.02 8 2.00 2.00 Comparable.>= 0.11 9.11 0.02 883 0.02 0.02 Array#[]= 0.11 9.13 0.02 1144 0.01 0.01 String#[] 0.11 9.14 0.02 883 0.02 0.02 File#exist? 0.11 9.16 0.02 1000 0.02 0.02 String#strip 0.11 9.17 0.01 2 7.50 7.50 Kernel.require 0.11 9.19 0.01 34 0.44 85.88 IO#open 0.00 9.19 0.00 8 0.00 0.00 Comparable.<= 0.00 9.19 0.00 2 0.00 0.00 Time#now 0.00 9.19 0.00 2 0.00 0.00 Time#initialize 0.00 9.19 0.00 2 0.00 0.00 Benchmark.times 0.00 9.19 0.00 2 0.00 0.00 Process.times 0.00 9.19 0.00 2 0.00 0.00 Struct#initialize 0.00 9.19 0.00 1 0.00 0.00 String#ljust 0.00 9.19 0.00 2 0.00 0.00 Class#new 0.00 9.19 0.00 1 0.00 0.00 Benchmark::Report#initialize 0.00 9.19 0.00 3 0.00 0.00 Kernel.print 0.00 9.19 0.00 1850 0.00 0.00 File#dirname 0.00 9.19 0.00 1003 0.00 0.00 IO#write 0.00 9.19 0.00 1 0.00 0.00 Kernel.iterator? 0.00 9.19 0.00 2 0.00 0.00 IO#sync= 0.00 9.19 0.00 1 0.00 0.00 IO#sync 0.00 9.19 0.00 33 0.00 88.03 Enumerable.to_a 0.00 9.19 0.00 34 0.00 0.00 IO#close 0.00 9.19 0.00 1 0.00 0.00 String#+ 0.00 9.19 0.00 1 0.00 0.00 String#* 0.00 9.19 0.00 1 0.00 0.00 Module#private 0.00 9.19 0.00 1 0.00 0.00 Encoding#default_external= 0.00 9.19 0.00 4 0.00 0.00 IO#set_encoding 0.00 9.19 0.00 1 0.00 0.00 String#== 0.00 9.19 0.00 1 0.00 0.00 Module#protected 0.00 9.19 0.00 1 0.00 0.00 Module#module_function 0.00 9.19 0.00 9 0.00 0.00 Module#attr_reader 0.00 9.19 0.00 4 0.00 0.00 Class#inherited 0.00 9.19 0.00 32 0.00 0.00 Module#method_added 0.00 9.19 0.00 144 0.00 0.00 String#length 0.00 9.19 0.00 34 0.00 0.00 File#initialize 0.00 9.19 0.00 9 0.00 0.00 BasicObject#singleton_method_added 0.00 9.19 0.00 2 0.00 0.00 Struct::Tms#utime 0.00 9.19 0.00 5 0.00 0.00 Float#- 0.00 9.19 0.00 2 0.00 0.00 Struct::Tms#stime 0.00 9.19 0.00 2 0.00 0.00 Struct::Tms#cutime 0.00 9.19 0.00 2 0.00 0.00 Struct::Tms#cstime 0.00 9.19 0.00 2 0.00 0.00 Time#to_f 0.00 9.19 0.00 3 0.00 0.00 Float#+ 0.00 9.19 0.00 1 0.00 0.00 Benchmark::Tms#initialize 0.00 9.19 0.00 1 0.00 0.00 Benchmark.measure 0.00 9.19 0.00 1 0.00 0.00 String#initialize_copy 0.00 9.19 0.00 1 0.00 0.00 Kernel.dup 0.00 9.19 0.00 7 0.00 0.00 String#gsub! 0.00 9.19 0.00 4 0.00 0.00 String#% 0.00 9.19 0.00 1 0.00 0.00 Kernel.format 0.00 9.19 0.00 1 0.00 0.00 Benchmark::Tms#format 0.00 9.19 0.00 1 0.00 0.00 Benchmark::Report#item 0.00 9.19 0.00 1 0.00 0.00 Module#=== 0.00 9.19 0.00 1 0.00 0.00 Benchmark.benchmark 0.00 9.19 0.00 1 0.00 0.00 Benchmark.bm 0.00 13.98 0.00 1 0.00 13984.00 #toplevel[/size] |
|
返回顶楼 | |
发表时间:2009-02-20
orange0513 写道 刚用netbeans打开data文件看了下,发现之前删文件开头空白的时候,完了还多出来一个.,不知道怎么搞的。现在好像没问题了。我用1000个随机ip看了下,没发现大些的UNKNOWN。用的WIN XP+RUBY1.9.1,data文件和结果在附件里,不过效率确实低了。
[size=xx-small] user system total real 13.969000 0.500000 14.469000 ( 14.515625) % cumulative self self total time seconds seconds calls ms/call ms/call name 20.77 2.90 2.90 33 88.03 88.03 IO#each 5.72 3.71 0.80 18071 0.04 0.06 BasicObject#!= 4.88 4.39 0.68 45503 0.01 0.01 Array#[] 4.81 5.06 0.67 2000 0.34 0.46 Array#each 3.48 5.55 0.49 13366 0.04 0.04 String#split 3.11 5.98 0.43 20071 0.02 0.02 Fixnum#== 2.88 6.38 0.40 11358 0.04 0.05 Comparable.> 2.78 6.77 0.39 6697 0.06 0.07 Comparable.< 2.45 7.12 0.34 992 0.35 0.77 IPParse#dichotomizing 2.12 7.41 0.30 18071 0.02 0.02 String#<=> 1.78 7.66 0.25 1000 0.25 0.88 IPParse#format 1.44 7.86 0.20 11366 0.02 0.02 Fixnum#/ 1.24 8.04 0.17 2000 0.09 0.12 Array#join 1.12 8.19 0.16 11366 0.01 0.01 Array#size 0.89 8.32 0.12 2001 0.06 0.09 Integer#times 0.87 8.44 0.12 5850 0.02 0.02 Fixnum#to_s 0.68 8.53 0.10 5000 0.02 0.02 String#to_i 0.66 8.63 0.09 1000 0.09 0.58 Object#randip 0.56 8.70 0.08 2000 0.04 0.38 Enumerable.inject 0.55 8.78 0.08 1000 0.08 0.08 IO#<< 0.44 8.84 0.06 4000 0.02 0.02 Array#<< 0.34 8.89 0.05 4000 0.01 0.01 String#rjust 0.34 8.94 0.05 4000 0.01 0.01 Kernel.rand 0.34 8.99 0.05 3000 0.02 0.02 String#to_s 0.23 9.02 0.03 4000 0.01 0.01 Fixnum#> 0.22 9.05 0.03 1000 0.03 0.17 Enumerator#each 0.21 9.08 0.03 1000 0.03 0.03 String#=~ 0.11 9.10 0.02 8 2.00 2.00 Comparable.>= 0.11 9.11 0.02 883 0.02 0.02 Array#[]= 0.11 9.13 0.02 1144 0.01 0.01 String#[] 0.11 9.14 0.02 883 0.02 0.02 File#exist? 0.11 9.16 0.02 1000 0.02 0.02 String#strip 0.11 9.17 0.01 2 7.50 7.50 Kernel.require 0.11 9.19 0.01 34 0.44 85.88 IO#open 0.00 9.19 0.00 8 0.00 0.00 Comparable.<= 0.00 9.19 0.00 2 0.00 0.00 Time#now 0.00 9.19 0.00 2 0.00 0.00 Time#initialize 0.00 9.19 0.00 2 0.00 0.00 Benchmark.times 0.00 9.19 0.00 2 0.00 0.00 Process.times 0.00 9.19 0.00 2 0.00 0.00 Struct#initialize 0.00 9.19 0.00 1 0.00 0.00 String#ljust 0.00 9.19 0.00 2 0.00 0.00 Class#new 0.00 9.19 0.00 1 0.00 0.00 Benchmark::Report#initialize 0.00 9.19 0.00 3 0.00 0.00 Kernel.print 0.00 9.19 0.00 1850 0.00 0.00 File#dirname 0.00 9.19 0.00 1003 0.00 0.00 IO#write 0.00 9.19 0.00 1 0.00 0.00 Kernel.iterator? 0.00 9.19 0.00 2 0.00 0.00 IO#sync= 0.00 9.19 0.00 1 0.00 0.00 IO#sync 0.00 9.19 0.00 33 0.00 88.03 Enumerable.to_a 0.00 9.19 0.00 34 0.00 0.00 IO#close 0.00 9.19 0.00 1 0.00 0.00 String#+ 0.00 9.19 0.00 1 0.00 0.00 String#* 0.00 9.19 0.00 1 0.00 0.00 Module#private 0.00 9.19 0.00 1 0.00 0.00 Encoding#default_external= 0.00 9.19 0.00 4 0.00 0.00 IO#set_encoding 0.00 9.19 0.00 1 0.00 0.00 String#== 0.00 9.19 0.00 1 0.00 0.00 Module#protected 0.00 9.19 0.00 1 0.00 0.00 Module#module_function 0.00 9.19 0.00 9 0.00 0.00 Module#attr_reader 0.00 9.19 0.00 4 0.00 0.00 Class#inherited 0.00 9.19 0.00 32 0.00 0.00 Module#method_added 0.00 9.19 0.00 144 0.00 0.00 String#length 0.00 9.19 0.00 34 0.00 0.00 File#initialize 0.00 9.19 0.00 9 0.00 0.00 BasicObject#singleton_method_added 0.00 9.19 0.00 2 0.00 0.00 Struct::Tms#utime 0.00 9.19 0.00 5 0.00 0.00 Float#- 0.00 9.19 0.00 2 0.00 0.00 Struct::Tms#stime 0.00 9.19 0.00 2 0.00 0.00 Struct::Tms#cutime 0.00 9.19 0.00 2 0.00 0.00 Struct::Tms#cstime 0.00 9.19 0.00 2 0.00 0.00 Time#to_f 0.00 9.19 0.00 3 0.00 0.00 Float#+ 0.00 9.19 0.00 1 0.00 0.00 Benchmark::Tms#initialize 0.00 9.19 0.00 1 0.00 0.00 Benchmark.measure 0.00 9.19 0.00 1 0.00 0.00 String#initialize_copy 0.00 9.19 0.00 1 0.00 0.00 Kernel.dup 0.00 9.19 0.00 7 0.00 0.00 String#gsub! 0.00 9.19 0.00 4 0.00 0.00 String#% 0.00 9.19 0.00 1 0.00 0.00 Kernel.format 0.00 9.19 0.00 1 0.00 0.00 Benchmark::Tms#format 0.00 9.19 0.00 1 0.00 0.00 Benchmark::Report#item 0.00 9.19 0.00 1 0.00 0.00 Module#=== 0.00 9.19 0.00 1 0.00 0.00 Benchmark.benchmark 0.00 9.19 0.00 1 0.00 0.00 Benchmark.bm 0.00 13.98 0.00 1 0.00 13984.00 #toplevel[/size] 嗯,我说的UNKNOW不是你说的UNKNOW的意思,是测试跑不过的意思, 格式化文件,我提供了一个里面提供了一个脚本,win下去掉第一行,跑跑应该可以的正确得到格式的 现在修改你的代码 raise 为return false测试全过了,程序应该可以跑出正确结果了 |
|
返回顶楼 | |
发表时间:2009-02-20
如果是一次性加载到内存,就没必要搞得太麻烦了。我以前做的是C++的版本,搞了一个std::map,然后lower_bound之类的(对应一个整数的key,ip地址转成int)查找,速度是非常的快。
|
|
返回顶楼 | |
发表时间:2009-02-20
这里的二分查文件,不停的文件seek,read,像服务器端没必要,一次读完就ok了。
|
|
返回顶楼 | |
发表时间:2009-02-22
当想通过shell调用,查询一个IP,就加载全部文件,就有点过了
这里是查询文件一次后缓存此文件。 |
|
返回顶楼 | |
发表时间:2009-02-26
为什么IP数据库不是标准的数据库格式,如mysql.
|
|
返回顶楼 | |
发表时间:2009-03-01
我以前用java写过一个利用纯真IP数据库查询IP地址。
程序在shell中输入: java IPSearcher 166 111 40 9 输出: 166.111.40.9 166.111.40.0 166.111.40.127 清华大学 建筑学院(建筑馆) 详细情况请看: http://archgo.com/index.php?option=com_content&view=article&id=60:ipjavaip&catid=29:software-express&Itemid=18 |
|
返回顶楼 | |
发表时间:2009-03-03
import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.RandomAccessFile; public class IPFinder { private String DbPath = "/IPData.Dat"; //IP纯真数据库 private String Country, LocalStr; private long IPN; private int RecordCount, CountryFlag; private long RangE, RangB, OffSet, StartIP, EndIP, FirstStartIP,LastStartIP, EndIPOff; private RandomAccessFile fis; private byte[] buff; /** * 用法: * IPFinder f = new IPFinder(path); * f.seek(ip); * String pc = f.getCountry();得到省市 * String lc = f.getLocal();得到地区 * * @param path */ public IPFinder(String path) { this.DbPath = path + "WEB-INF/classes/com/hwxsoft/qlfw/common" + this.DbPath; } private long B2L(byte[] b) { long ret = 0; for (int i = 0; i < b.length; i++) { long t = 1L; for (int j = 0; j < i; j++) t = t * 256L; ret += ((b[i] < 0) ? 256 + b[i] : b[i]) * t; } return ret; } private long ipToInt(String ip) { String[] arr = ip.split("\\."); long ret = 0; for (int i = 0; i < arr.length; i++) { long l = 1; for (int j = 0; j < i; j++) l *= 256; try { ret += Long.parseLong(arr[arr.length - i - 1]) * l; } catch (Exception e) { ret += 0; } } return ret; } public void seek(String ip) throws Exception { this.IPN = ipToInt(ip); fis = new RandomAccessFile(this.DbPath, "r"); buff = new byte[4]; fis.seek(0); fis.read(buff); FirstStartIP = this.B2L(buff); fis.read(buff); LastStartIP = this.B2L(buff); RecordCount = (int) ((LastStartIP - FirstStartIP) / 7); if (RecordCount <= 1) { LocalStr = Country = "未知"; throw new Exception(); } RangB = 0; RangE = RecordCount; long RecNo; do { RecNo = (RangB + RangE) / 2; getStartIP(RecNo); if (IPN == StartIP) { RangB = RecNo; break; } if (IPN > StartIP) RangB = RecNo; else RangE = RecNo; } while (RangB < RangE - 1); getStartIP(RangB); getEndIP(); getCountry(IPN); fis.close(); } private String getFlagStr(long OffSet) throws IOException { int flag = 0; do { fis.seek(OffSet); buff = new byte[1]; fis.read(buff); flag = (buff[0] < 0) ? 256 + buff[0] : buff[0]; if (flag == 1 || flag == 2) { buff = new byte[3]; fis.read(buff); if (flag == 2) { CountryFlag = 2; EndIPOff = OffSet - 4; } OffSet = this.B2L(buff); } else break; } while (true); if (OffSet < 12) { return ""; } else { fis.seek(OffSet); return getStr(); } } private String getStr() throws IOException { long l = fis.length(); ByteArrayOutputStream byteout = new ByteArrayOutputStream(); byte c = fis.readByte(); do { byteout.write(c); c = fis.readByte(); } while (c != 0 && fis.getFilePointer() < l); return byteout.toString(); } /** * 得到省市 * @param ip * @throws IOException */ private void getCountry(long ip) throws IOException { if (CountryFlag == 1 || CountryFlag == 2) { Country = getFlagStr(EndIPOff + 4); if (CountryFlag == 1) { LocalStr = getFlagStr(fis.getFilePointer()); if (IPN >= ipToInt("255.255.255.0") && IPN <= ipToInt("255.255.255.255")) { LocalStr = getFlagStr(EndIPOff + 21); Country = getFlagStr(EndIPOff + 12); } } else { LocalStr = getFlagStr(EndIPOff + ![]() } } else { Country = getFlagStr(EndIPOff + 4); LocalStr = getFlagStr(fis.getFilePointer()); } } private long getEndIP() throws IOException { fis.seek(EndIPOff); buff = new byte[4]; fis.read(buff); EndIP = this.B2L(buff); buff = new byte[1]; fis.read(buff); CountryFlag = (buff[0] < 0) ? 256 + buff[0] : buff[0]; return EndIP; } private long getStartIP(long RecNo) throws IOException { OffSet = FirstStartIP + RecNo * 7; fis.seek(OffSet); buff = new byte[4]; fis.read(buff); StartIP = this.B2L(buff); buff = new byte[3]; fis.read(buff); EndIPOff = this.B2L(buff); return StartIP; } /** * 得到地段 * @return */ public String getLocal() { return this.LocalStr; } public String getCountry() { return this.Country; } public void setPath(String path) { this.DbPath = path; } } |
|
返回顶楼 | |