Php 查找两个IPv4地址之间的网络距离(不是地理距离)

Php 查找两个IPv4地址之间的网络距离(不是地理距离),php,python,networking,Php,Python,Networking,给定一个IPv4地址(针)和一个未排序的IPv4地址数组(干草堆),我如何通过编程确定给定干草堆中哪一个地址距离针最近(网络方面,而不是地理上) 由于我无法访问每个地址的网络掩码,因此解决方案应该忽略网络掩码和跟踪路由类似选项 使用了各种各样的地址,我的意思是:专用、保留、广播、局域网和广域网 欢迎以理论、伪代码、python、php或perl的形式提供任何帮助 这个问题大致相似,但它确实解决了这个问题。我首先将数组划分为若干组,考虑保留的ip地址()。然后我会把它们按区域分开。也许一些树状结构

给定一个IPv4地址(针)和一个未排序的IPv4地址数组(干草堆),我如何通过编程确定给定干草堆中哪一个地址距离针最近(网络方面,而不是地理上)

由于我无法访问每个地址的网络掩码,因此解决方案应该忽略网络掩码和跟踪路由类似选项

使用了各种各样的地址,我的意思是:专用、保留、广播、局域网和广域网

欢迎以理论、伪代码、python、php或perl的形式提供任何帮助


这个问题大致相似,但它确实解决了这个问题。

我首先将数组划分为若干组,考虑保留的ip地址()。然后我会把它们按区域分开。也许一些树状结构就足够了:根节点是顶级区域,然后,接近叶子的节点用于较小的区域。

我首先将数组分成组,考虑保留的ip地址()。然后我会把它们按区域分开。也许一些树状结构就足够了:根节点是顶级区域,然后,靠近叶子,它会用于较小的区域。

我仍然不太确定你在问什么,但基于你的评论


@彼得吉布森我指的是192.168.1.101更接近 192.168.56.1,而不是172.30.130.66。192.168.1.254比192.168.2.1更接近192.168.1.240

对于距离函数,您可以尝试以下python代码:

import socket
def dist(a, b):
    def to_num(addr):
        # parse the address string into integer quads
        quads = map(ord, socket.inet_aton(addr))
        # spread the quads out 
        return reduce(lambda x,y: x * 0x10000 + y, quads)
    return abs(to_num(a) - to_num(b))
返回的数字相当随意,但应足以满足基本需求。我仍然不确定它应该如何处理广播地址等

一些例子:

>>> dist('192.168.1.254', '192.168.1.240')
14L
>>> dist('192.168.1.254', '192.168.2.1')
65283L
>>> dist('192.168.1.101', '192.168.56.1')
3604380L
>>> dist('192.168.1.101', '172.30.130.66')
5630092231245859L

我仍然不太清楚你在问什么,但根据你的评论


@彼得吉布森我指的是192.168.1.101更接近 192.168.56.1,而不是172.30.130.66。192.168.1.254比192.168.2.1更接近192.168.1.240

对于距离函数,您可以尝试以下python代码:

import socket
def dist(a, b):
    def to_num(addr):
        # parse the address string into integer quads
        quads = map(ord, socket.inet_aton(addr))
        # spread the quads out 
        return reduce(lambda x,y: x * 0x10000 + y, quads)
    return abs(to_num(a) - to_num(b))
返回的数字相当随意,但应足以满足基本需求。我仍然不确定它应该如何处理广播地址等

一些例子:

>>> dist('192.168.1.254', '192.168.1.240')
14L
>>> dist('192.168.1.254', '192.168.2.1')
65283L
>>> dist('192.168.1.101', '192.168.56.1')
3604380L
>>> dist('192.168.1.101', '172.30.130.66')
5630092231245859L

这不是最漂亮的方法,但由于不需要非常高的性能,它就可以完成这项工作

<?php

$ip = isset($argv[1]) ? $argv[1] : '192.168.1.254';
printf("Input => %s => %u\n", $ip, ip2long($ip));

var_dump(
    net_distance(
        $ip,
        array(
            '87.86.85.84',
            '173.194.41.170',
            '192.168.1.150',
            '192.168.1.245',
            '192.168.2.2',
            '192.168.56.50',
        )
    )
);

// The problem assumes that:
// a) any local (127) and lan (10,172,192) IPs are closer to each other than to any wan IP.
// b) any wan IP is closer to local and lan IPs than to other wan IP.
function net_distance($src_ip, $list)
{
  if (in_array($src_ip, $list)) {
    return $src_ip; // exact match
  }

  list($a0, $b0, $c0, $d0) = explode('.', $src_ip, 4);

  $triples = array();
  $doubles = array();
  $singles = array();

  foreach($list as $ip) {
    list($a1, $b1, $c1, $d1) = explode('.', $ip, 4);

    if ($a0 == $a1 && $b0 == $b1 && $c0 == $c1) { // match A.B.C.x
      $triples[] = $ip;
    }
    elseif ($a0 == $a1 && $b0 == $b1) { // match A.B.x.y
      $doubles[] = $ip;
    }
    elseif ($a0 == $a1 || (in_array($a0, array(127, 10, 172, 192)) && in_array($a1, array(127, 10, 172, 192)))) {
      $singles[] = $ip; // match A.x.y.z or both As are *likely* to be lan addresses
    }
  }

  if (count($triples) > 0) {
    $list = $triples;
  }
  elseif (count($doubles) > 0) {
    $list = $doubles;
  }
  elseif (count($singles) > 0) {
    $list = $singles;
  }

  $min = PHP_INT_MAX;
  $rtn = false;
  $l1 = ip2long($src_ip);
  foreach($list as $ip)
  {
    $l2 = ip2long($ip);
    $d = ($l1 > $l2) ? $l1 - $l2 : $l2 - $l1;
    // echo "\t" . str_pad($ip, 15, ' ', STR_PAD_RIGHT) . " => $l2 => $d\n";
    if ($min > $d) {
      $rtn = $ip;
      $min = $d;
    }
  }

  return $rtn;
}

不是最漂亮的方法,但由于不需要非常高的性能,它就可以完成这项工作

<?php

$ip = isset($argv[1]) ? $argv[1] : '192.168.1.254';
printf("Input => %s => %u\n", $ip, ip2long($ip));

var_dump(
    net_distance(
        $ip,
        array(
            '87.86.85.84',
            '173.194.41.170',
            '192.168.1.150',
            '192.168.1.245',
            '192.168.2.2',
            '192.168.56.50',
        )
    )
);

// The problem assumes that:
// a) any local (127) and lan (10,172,192) IPs are closer to each other than to any wan IP.
// b) any wan IP is closer to local and lan IPs than to other wan IP.
function net_distance($src_ip, $list)
{
  if (in_array($src_ip, $list)) {
    return $src_ip; // exact match
  }

  list($a0, $b0, $c0, $d0) = explode('.', $src_ip, 4);

  $triples = array();
  $doubles = array();
  $singles = array();

  foreach($list as $ip) {
    list($a1, $b1, $c1, $d1) = explode('.', $ip, 4);

    if ($a0 == $a1 && $b0 == $b1 && $c0 == $c1) { // match A.B.C.x
      $triples[] = $ip;
    }
    elseif ($a0 == $a1 && $b0 == $b1) { // match A.B.x.y
      $doubles[] = $ip;
    }
    elseif ($a0 == $a1 || (in_array($a0, array(127, 10, 172, 192)) && in_array($a1, array(127, 10, 172, 192)))) {
      $singles[] = $ip; // match A.x.y.z or both As are *likely* to be lan addresses
    }
  }

  if (count($triples) > 0) {
    $list = $triples;
  }
  elseif (count($doubles) > 0) {
    $list = $doubles;
  }
  elseif (count($singles) > 0) {
    $list = $singles;
  }

  $min = PHP_INT_MAX;
  $rtn = false;
  $l1 = ip2long($src_ip);
  foreach($list as $ip)
  {
    $l2 = ip2long($ip);
    $d = ($l1 > $l2) ? $l1 - $l2 : $l2 - $l1;
    // echo "\t" . str_pad($ip, 15, ' ', STR_PAD_RIGHT) . " => $l2 => $d\n";
    if ($min > $d) {
      $rtn = $ip;
      $min = $d;
    }
  }

  return $rtn;
}


你能澄清一下你所说的“最近的网络”是什么意思吗?不确定你所说的最近的是什么意思?至于您的各种地址,它们都只是没有网络掩码的IP,地址的网络/广播状态未知。局域网/广域网用于人类理解。基本上,IP就是IP就是IP。你的意思是说,如果给出10.10.0.1的指针和“10.20.0.1、10.30.0.1、10.10.0.0、10.10.0.3”的干草堆,程序会吐回10.10.0.0吗?因为它是数值上最接近的?也是像perl(和python)中的Net::Cidr一样的东西,但如果没有掩码,它会变得很困难。@PeterGibson我的意思是,192.168.1.101更接近192.168.56.1,而不是172.30.130.66。192.168.1.254比192.168.2更接近192.168.1.240。1@msantos但您要应用什么规则,因为如果忽略子网掩码,192.168.2.1是192.168.1.254中的2个IP和1.240中的14个IP(假设为>/23掩码)。在您的示例中,您仍在应用子网。如果您只是在寻找“距离”,请将两个IP转换为整数,并查看它们之间差值的绝对值。您能否澄清“最近的网络”是什么意思?不确定“最近的网络”到底是什么意思?至于您的各种地址,它们都只是没有网络掩码的IP,地址的网络/广播状态未知。局域网/广域网用于人类理解。基本上,IP就是IP就是IP。你的意思是说,如果给出10.10.0.1的指针和“10.20.0.1、10.30.0.1、10.10.0.0、10.10.0.3”的干草堆,程序会吐回10.10.0.0吗?因为它是数值上最接近的?也是像perl(和python)中的Net::Cidr一样的东西,但如果没有掩码,它会变得很困难。@PeterGibson我的意思是,192.168.1.101更接近192.168.56.1,而不是172.30.130.66。192.168.1.254比192.168.2更接近192.168.1.240。1@msantos但您要应用什么规则,因为如果忽略子网掩码,192.168.2.1是192.168.1.254中的2个IP和1.240中的14个IP(假设为>/23掩码)。在您的示例中,您仍在应用子网。如果只是查找“距离”,请将两个IP转换为整数,并查看它们之间差值的绝对值,则问题将忽略IP是否为广播、多播、专用等。。它以完全相同的方式处理从0.0.0.0到255.255.255.255的所有IP。目标不是找到最短的路线。但实际上是要找出两个地址之间的IP数。哦,类似这样的距离('192.168.0.1','192.168.0.5')==4
?然后将地址转换为十六进制,并使用一些二进制搜索树('192.168.0.1','192.168.0.5')==4和距离('192.168.1.1','192.168.0.252')==4,但由于192.168.0.1和192.168.0.252可能位于同一网络中,因此它们是最近的节点。这是一个很大的假设,即它们可能位于同一网络中。也许在soho路由器的世界里,它们是。但是,除了/24之外,还有很多网络使用掩码。这个问题忽略了IP是否是广播的、多播的、私有的等等。。它以完全相同的方式处理从0.0.0.0到255.255.255.255的所有IP。目标不是找到最短的路线。但实际上是要找出两个地址之间的IP数。哦,类似这样的距离('192.168.0.1','192.168.0.5')==4
?然后将地址转换为十六进制,并使用一些二进制搜索树('192.168.0.1','192.168.0.5')==4和距离('192.168.1.1','192.168.0.252')==4,但是因为192.168.0.1和192.168.0.252是l