进行简单 IP 地址比较的最高效方法是啥?
Posted
技术标签:
【中文标题】进行简单 IP 地址比较的最高效方法是啥?【英文标题】:What's the most performant way to do simple IP address comparisons?进行简单 IP 地址比较的最高效方法是什么? 【发布时间】:2015-09-29 14:40:57 【问题描述】:我在 node.js 中工作,我想做类似下面的伪代码...
let ip_range = [50.1.100.1, 51.1.30.1]; // Converted from a CIDR string.
let ip_address = 50.2.200.2; // Input IP address.
if(ip_address >= ip_range[0] && ip_address <= ip_range[1])
block(ip_address);
对最快的方法有什么想法吗?
我已经查看了 cidr-js,它提供了 CIDR 转换功能,但不提供 IP 地址比较。似乎 node-ip 可能是一个不错的选择。
谢谢!
【问题讨论】:
为什么要放弃 CIDR? CIDR 是位掩码,允许您只比较前 N 位,这与您将获得的效率差不多。 当然。我实际上已经考虑了一点。 CIDR 作为字符串输入。这是位掩码的问题还是我应该只转换为数字? 假设您只是在处理 IPv4 地址,只需将地址转换为 32 位整数并进行比较。 您需要将字符串转换为数字,然后您可以在 CIDR 和 IP 之间进行按位比较(我相信是(cidr & ip) == cidr
)。
我相信这行得通。现在我只是将它们转换为多头并以这种方式进行比较。感谢您的帮助!
【参考方案1】:
我们所知道的 IP 地址只是 32 位数值的字符串表示形式。
通过将字符串表示形式转换回其数值,使用本机数字比较检查地址范围内的成员关系变得非常容易,如下面的代码所示:
var atoi = function atoi(addr)
var parts = addr.split('.').map(function(str)
return parseInt(str);
);
return (parts[0] ? parts[0] << 24 : 0) +
(parts[1] ? parts[1] << 16 : 0) +
(parts[2] ? parts[2] << 8 : 0) +
parts[3];
;
var checkIpaddrInRange = function checkIpaddrInRange(ipaddr, start, end)
var num = atoi(ipaddr);
return (num >= atoi(start)) && (num <= atoi(end));
checkIpaddrInRange('10.0.1.1', '10.0.0.1', '10.0.2.1'); // => true
checkIpaddrInRange('10.0.3.1', '10.0.0.1', '10.0.2.1'); // => false
见fiddle。
这里是同样的东西,完整的注释和正确的错误检查:
/**
* Checks if ipaddr is valid.
* @property string ipaddr
* @throws Error
*/
var assertIsIpaddr = function assertIsIpaddr(ipaddr)
if('string' !== typeof ipaddr && ipaddr)
throw new Error('ipaddr must be a non-empty string');
var parts=ipaddr.split(/\./);
if(parts.length !== 4)
throw new Error('ipaddr must have four octets');
var i=0;
parts.map(function(str)
var val=parseInt(str),
octet = 4 - i++;;
if(val < 0 || val > 255)
throw new Error('octet '+octet+' must be between 0 and 255');
);
;
/**
* Converts an ipaddr to a 32bit integer value.
* @property string addr - the ipaddr to convert
* @returns number
*/
var atoi = function atoi(addr)
// test for validity - will throw!
assertIsIpaddr(addr);
// convert octets to numbers
var parts = addr.split('.').map(function(str)
return parseInt(str);
);
// construct result
var result = (parts[0] ? parts[0] << 24 : 0) + // if > 0, shift 4th octet left by 24
(parts[1] ? parts[1] << 16 : 0) + // if > 0, shift 3rd octet left by 16
(parts[2] ? parts[2] << 8 : 0) + // if > 0, shift 2nd octet left by 8
parts[3];
// note that if all octets are 255, result will overflow
// javascript (32bit) number to become -1, so we have to
// special case it. I think throwing an error here is a
// reasonable solution, since 255.255.255.255 is actually
// a broadcast addr.
if(result < 0)
throw new Error('255.255.255.255 is not a legal host ipaddr');
return result;
;
/**
* Checks ipaddr membership within a range of ipaddrs.
* @property string ipaddr - ipaddr to check
* @property string start - the start of the ipaddr range
* @property string end - the end of the ipaddr range
* @returns boolean - true if ipaddr is between start and end (inclusive)
*/
var checkIpaddrInRange = function checkIpaddrInRange(ipaddr, start, end)
var num = atoi(ipaddr);
return (num >= atoi(start)) && (num <= atoi(end));
// OK, test it out...
checkIpaddrInRange('10.0.1.1','10.0.0.1','10.0.2.1'); // => true
checkIpaddrInRange('10.0.3.1','10.0.0.1','10.0.2.1'); // => false
【讨论】:
如果第一部分大于 127,则此代码不起作用。128 << 24
给出-2147483648
。为了使它工作,我用乘法const result = (parts[0] ? parts[0] * Math.pow(256, 3) : 0) + (parts[1] ? parts[1] * Math.pow(256, 2) : 0) + (parts[2] ? parts[2] * Math.pow(256, 1) : 0) + parts[3];
替换了位操作【参考方案2】:
可能有一种更简单的方法来做到这一点,但这是我可能会采用的方法。
function pad(n)
return (n.length < 3) ? pad('0' + n) : n;
// return a number with each part of the IP padded out to 3 digits
function convert(ip)
return parseInt(ip.split('.').map(function (el)
return pad(el);
).join(''), 10);
// check the number against the range
function check(range, ip)
ip = convert(ip);
return ip >= range[0] && ip <= range[1];
// convert the range
var range = ['50.1.100.1', '51.1.30.1'].map(function (el)
return convert(el);
);
check(range, '51.1.20.2'); // true
check(range, '51.1.40.2'); // false
DEMO
【讨论】:
【参考方案3】:使用ipaddr.js (npm i ipaddr.js
):
const ipaddr = require('ipaddr.js')
const addr = ipaddr.parse('2001:db8:1234::1')
addr.match(ipaddr.parseCIDR('2001:db8::/32')) // => true
或者:
const rangeList =
documentationOnly: [ ipaddr.parse('2001:db8::'), 32 ],
tunnelProviders: [
[ ipaddr.parse('2001:470::'), 32 ], // he.net
[ ipaddr.parse('2001:5c0::'), 32 ] // freenet6
]
ipaddr.subnetMatch(ipaddr.parse('2001:470:8:66::1'), rangeList, 'unknown') // => "tunnelProviders"
Official documentation
【讨论】:
【参考方案4】:也许是一种简短的方法
const ipAddress = '10.120.238.254';
const asNumber = ipAdress.split('.')
.map(p => parseInt(p))
.reverse()
.reduce((acc,val,i) => acc+(val*(256**i)),0)
然后比较数字
【讨论】:
以上是关于进行简单 IP 地址比较的最高效方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章