论坛首页 编程语言技术论坛

IPParse: IP 地址查询

浏览 32786 次
该帖已经被评为良好帖
作者 正文
   发表时间: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* 秒
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缓存,估计会慢很多。
0 请登录后投票
   发表时间: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]
0 请登录后投票
   发表时间: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测试全过了,程序应该可以跑出正确结果了
0 请登录后投票
   发表时间:2009-02-20  
如果是一次性加载到内存,就没必要搞得太麻烦了。我以前做的是C++的版本,搞了一个std::map,然后lower_bound之类的(对应一个整数的key,ip地址转成int)查找,速度是非常的快。
0 请登录后投票
   发表时间:2009-02-20  
这里的二分查文件,不停的文件seek,read,像服务器端没必要,一次读完就ok了。
0 请登录后投票
   发表时间:2009-02-22  
当想通过shell调用,查询一个IP,就加载全部文件,就有点过了

这里是查询文件一次后缓存此文件。
0 请登录后投票
   发表时间:2009-02-26  
为什么IP数据库不是标准的数据库格式,如mysql.
0 请登录后投票
   发表时间: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
0 请登录后投票
   发表时间: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;
}
}
0 请登录后投票
论坛首页 编程语言技术版

跳转论坛:
Global site tag (gtag.js) - Google Analytics