对于我的服务器应用程序,我需要检查一个 IP 地址是否在我们的黑名单中。
比较IP地址的最有效方法是什么?将 IP 地址转换为整数并进行比较是否有效?
取决于您使用的语言,但 IP 地址通常存储为 32 位无符号整数,至少在网络层是这样,因此比较速度非常快。即使不是,除非您正在设计高性能分组交换应用程序,否则它不太可能成为性能瓶颈。避免过早的优化——为可测试性和可扩展性设计你的程序,如果你有性能问题,那么你可以使用分析器来查看瓶颈在哪里。
编辑:澄清一下,IPv4 地址存储为 32 位整数,外加网络掩码(IP 地址比较不需要)。如果您使用较新且目前更稀有的 IPv6,则地址将是 128 位长。
32 位整数是要走的路——直到您开始处理 128 位 IPv6 地址。
您的意思是您应该将其作为文本字符串进行比较还是将 int 转换为 int 并作为 int 进行比较?
这通常不是这种查找的瓶颈。您可以尝试实现这两种方法,看看哪一种运行得更快。
IP 地址查找的真正问题通常是进行有效的查询,利用您处理 IP 地址而不仅仅是随机数这一事实。为此,您可以查找LC trie。
显然,只有当您的黑名单包含数万或数百万个条目时,您才会对此感兴趣。如果它只有 10-20 个条目,则应该首选线性搜索,实际上更有趣的问题是文本比较与整数比较。
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);
}
因为你得到了地址字节。
是的,我发现要高效,它会很长,当然你必须以整数形式索引列入黑名单的 IP。
使用 PeerGuardian 之类的工具,它在驱动程序级别禁止传入 TCP/IP 连接到黑名单上的 IP。高度安全,不需要代码(可以说:高度安全,因为不需要代码)。
我已经完成了这项工作并且我已经对其进行了测试,使用无符号整数(32 位)是最快的 - 我假设您正在将它与字符串表示进行比较。
另一件可能对您有所帮助的事情是在创建表格时,过去我有 2 个列:LowIP 和 HighIP;这样,我就能够通过 1 个记录条目将整个 IP 范围列入黑名单,并且通过检查范围内的 IP 仍然可以获得良好的性能。
我曾经继承过代码,有人认为将 IP 地址存储为 4 个 int 是一件非常好的事情,除了他们把所有时间都花在了与 int 之间的转换上。
将它们作为字符串保存在数据库中要容易得多,而且只需要一个索引。您会惊讶于 sql server 可以索引字符串而不是 4 列整数。但是这个 IP 列表不是用于黑名单的。数据库往返是相当昂贵的。
如果数据库太过分了,请将它们存储在内存中的字典中,但这只是一个猜测,因为我们不知道您需要比较多少。由于大多数哈希码是 32 位整数,而 IPv4 地址是 32 位,因此 IP 地址本身可能只是一个很好的哈希码。
但正如其他人指出的那样,最好的选择可能是减少服务器上的负载并购买专用硬件。也许您将最近列入黑名单的 IP 保存在内存中,并定期将新 IP 发布到路由器。
如果您是试图在路由器中制作一些软件的人,那么您需要找出您的数据结构书并创建类似 b-tree 的东西。
您是否存在效率方面的问题?
如果是这样,那么一定要发布代码(或伪代码),我们可以挑选尸体。
如果不是,那么我建议尝试一些简单的方法,例如将条目存储在排序列表中并使用您环境中现有的Sort()
和Find()
.
整数比较比字符串比较快得多。
如果将整数存储在排序列表中,则可以比未排序列表更快地找到它们。
如果您将 IP 地址作为字符串接收,则将其与字符串进行比较可能比将其转换为整数表示更有效
但是我会确定这两种解决方案,如果几毫秒(纳秒!)对这个操作很重要;-)
以下是我在 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;
}