13

对于我的服务器应用程序,我需要检查一个 IP 地址是否在我们的黑名单中。

比较IP地址的最有效方法是什么?将 IP 地址转换为整数并进行比较是否有效?

4

13 回答 13

30

取决于您使用的语言,但 IP 地址通常存储为 32 位无符号整数,至少在网络层是这样,因此比较速度非常快。即使不是,除非您正在设计高性能分组交换应用程序,否则它不太可能成为性能瓶颈。避免过早的优化——为可测试性和可扩展性设计你的程序,如果你有性能问题,那么你可以使用分析器来查看瓶颈在哪里。

编辑:澄清一下,IPv4 地址存储为 32 位整数,外加网络掩码(IP 地址比较不需要)。如果您使用较新且目前更稀有的 IPv6,则地址将是 128 位长。

于 2008-10-02T03:31:44.663 回答
7

32 位整数是要走的路——直到您开始处理 128 位 IPv6 地址。

于 2008-10-02T03:33:20.963 回答
6

您的意思是您应该将其作为文本字符串进行比较还是将 int 转换为 int 并作为 int 进行比较?

这通常不是这种查找的瓶颈。您可以尝试实现这两种方法,看看哪一种运行得更快。

IP 地址查找的真正问题通常是进行有效的查询,利用您处理 IP 地址而不仅仅是随机数这一事实。为此,您可以查找LC trie

显然,只有当您的黑名单包含数万或数百万个条目时,您才会对此感兴趣。如果它只有 10-20 个条目,则应该首选线性搜索,实际上更有趣的问题是文本比较与整数比较。

于 2008-10-02T03:35:56.977 回答
6
static public bool IsEqual(string ToCompare,
                                      string CompareAgainst)
  {

     return IPAddressToLongBackwards(ToCompare)==IPAddressToLongBackwards(CompareAgainst);
  }

static private uint IPAddressToLongBackwards(string IPAddr)
  {
     System.Net.IPAddress oIP=System.Net.IPAddress.Parse(IPAddr);
     byte[] byteIP=oIP.GetAddressBytes();


     uint ip=(uint)byteIP[0]<<24;
     ip+=(uint)byteIP[1]<<16;
     ip+=(uint)byteIP[2]<<8;
     ip+=(uint)byteIP[3];

     return ip;
  }

如果我理解正确,这是比较两个 IP 地址的代码。你想要这个吗?您可以进一步执行以下操作:

static public bool IsGreater(string ToCompare,
                               string CompareAgainst)
  {

     return IPAddressToLongBackwards(ToCompare)>
        IPAddressToLongBackwards(CompareAgainst);
  }

因为你得到了地址字节。

于 2009-10-28T14:33:57.210 回答
4

是的,我发现要高效,它会很长,当然你必须以整数形式索引列入黑名单的 IP。

于 2008-10-02T03:32:26.270 回答
3

使用 PeerGuardian 之类的工具,它在驱动程序级别禁止传入 TCP/IP 连接到黑名单上的 IP。高度安全,不需要代码(可以说:高度安全,因为不需要代码)。

于 2008-10-02T03:34:24.330 回答
3

我已经完成了这项工作并且我已经对其进行了测试,使用无符号整数(32 位)是最快的 - 我假设您正在将它与字符串表示进行比较。

另一件可能对您有所帮助的事情是在创建表格时,过去我有 2 个列:LowIP 和 HighIP;这样,我就能够通过 1 个记录条目将整个 IP 范围列入黑名单,并且通过检查范围内的 IP 仍然可以获得良好的性能。

于 2008-10-02T04:28:02.953 回答
3

我曾经继承过代码,有人认为将 IP 地址存储为 4 个 int 是一件非常好的事情,除了他们把所有时间都花在了与 int 之间的转换上。

将它们作为字符串保存在数据库中要容易得多,而且只需要一个索引。您会惊讶于 sql server 可以索引字符串而不是 4 列整数。但是这个 IP 列表不是用于黑名单的。数据库往返是相当昂贵的。

如果数据库太过分了,请将它们存储在内存中的字典中,但这只是一个猜测,因为我们不知道您需要比较多少。由于大多数哈希码是 32 位整数,而 IPv4 地址是 32 位,因此 IP 地址本身可能只是一个很好的哈希码。

但正如其他人指出的那样,最好的选择可能是减少服务器上的负载并购买专用硬件。也许您将最近列入黑名单的 IP 保存在内存中,并定期将新 IP 发布到路由器。

如果您是试图在路由器中制作一些软件的人,那么您需要找出您的数据结构书并创建类似 b-tree 的东西。

于 2008-10-02T05:08:43.447 回答
3

Radix 或 PATRICIA Trie 是为此的最佳结构。

查看流工具的 C 源代码: http ://www.splintered.net/sw/flow-tools/

几年前我在这个工作。

于 2009-04-25T10:29:33.130 回答
2

您是否存在效率方面的问题?

如果是这样,那么一定要发布代码(或伪代码),我们可以挑选尸体。

如果不是,那么我建议尝试一些简单的方法,例如将条目存储在排序列表中并使用您环境中现有的Sort()Find().

于 2008-10-02T03:35:51.127 回答
2

整数比较比字符串比较快得多。

如果将整数存储在排序列表中,则可以比未排序列表更快地找到它们。

于 2008-10-02T03:44:48.470 回答
1

如果您将 IP 地址作为字符串接收,则将其与字符串进行比较可能比将其转换为整数表示更有效

但是我会确定这两种解决方案,如果几毫秒(纳秒!)对这个操作很重要;-)

于 2008-10-02T03:34:44.907 回答
1

以下是我在 JavaScript 中使用过的

function isValidIPv4Range(iPv4Range = '') {
  if (IP_V4_RANGE_REGEX.test(iPv4Range)) {
    const [fromIp, toIp] = iPv4Range.split('-');

    if (!isValidOctets(fromIp) || !isValidOctets(toIp)) {
      return false;
    }

    const convertToNumericWeight = ip => {
      const [octet1, octet2, octet3, octet4] = ip.split('.').map(parseInt);

      return octet4 + (octet3 * 256) + (octet2 * 256 * 256) + (octet1 * 256 * 256 * 256);
    };

    return convertToNumericWeight(fromIp) < convertToNumericWeight(toIp);
  }
  return false;
}

于 2020-04-09T07:20:37.410 回答