存储为int的IP地址会导致溢出?

时间:2011-11-12 16:08:18

标签: javascript

我正在node.js中编写聊天服务器,我想将连接用户的IP地址存储在mysql数据库中作为(无符号)整数。 我编写了一个javascript方法将ip-address转换为字符串为整数。然而,我得到了一些奇怪的结果。

这是我的代码:

function ipToInt(ip) {
    var parts = ip.split(".");
    var res = 0;

    res += parseInt(parts[0], 10) << 24;
    res += parseInt(parts[1], 10) << 16;
    res += parseInt(parts[2], 10) << 8;
    res += parseInt(parts[3], 10);

    return res;
}

当我运行调用方法ipToInt("192.168.2.44");时,我得到的结果是-1062731220。 看起来好像发生了溢出,这很奇怪,因为预期的输出(3232236076)在javascript(2 ^ 52)的数字范围内。

当我以二进制形式检查-1062731220时,我可以看到保留了3232236076,但却填充了前导1。

我不确定,但我认为问题在于有符号与无符号整数。

你们任何人都可以解释发生了什么吗? 并且可能如何将-1062731220解析回字符串ip?

12 个答案:

答案 0 :(得分:41)

为什么转换后的IP为负?

这不是溢出。 IP地址的第一部分是192,它以二进制形式转换为11000000。然后你将它一直向左移动。当32位数字的最左边位置有1时,它是负数。

如何转换回字符串?

执行与从字符串转换相反的操作,但反之亦然。向右移(和面具)!

function intToIP(int) {
    var part1 = int & 255;
    var part2 = ((int >> 8) & 255);
    var part3 = ((int >> 16) & 255);
    var part4 = ((int >> 24) & 255);

    return part4 + "." + part3 + "." + part2 + "." + part1;
}

为什么重新发明轮子?来自Google:

或者,您可以使用我在此处找到的内容:
http://javascript.about.com/library/blipconvert.htm

function dot2num(dot) 
{
    var d = dot.split('.');
    return ((((((+d[0])*256)+(+d[1]))*256)+(+d[2]))*256)+(+d[3]);
}

function num2dot(num) 
{
    var d = num%256;
    for (var i = 3; i > 0; i--) 
    { 
        num = Math.floor(num/256);
        d = num%256 + '.' + d;
    }
    return d;
}

答案 1 :(得分:6)

“&lt;&lt;”的结果根据规范,运算符始终是带符号的32位整数。

当您转回时,请使用“&gt;&gt;&gt;”做无条件的右移。

答案 2 :(得分:3)

您可能还会发现此模式很有用:

ip.toLong = function toInt(ip){
  var ipl=0;
  ip.split('.').forEach(function( octet ) {
      ipl<<=8;
      ipl+=parseInt(octet);
  });
  return(ipl >>>0);
};

ip.fromLong = function fromInt(ipl){
  return ( (ipl>>>24) +'.' +
      (ipl>>16 & 255) +'.' +
      (ipl>>8 & 255) +'.' +
      (ipl & 255) );
};

如果您正在使用像node.js这样的东西,您可以通过Npm之类的东西添加功能,那么您可以这样做:

npm install ip

从以下来源获取该功能:
https://github.com/indutny/node-ip/blob/master/lib/ip.js

您还将获得许多其他IP实用程序功能。

答案 3 :(得分:1)

你向左移动得到原始数字 - 无论符号如何,这只是4位。

向右移动以返回IP。标志是什么并不重要。

答案 4 :(得分:1)

单行:

const ipToLong = ip => ip.split('.').map(parseFloat).reduce((total, part) => total * 256 + part);

答案 5 :(得分:0)

V4空间中的IP地址是无符号的32位数,因此FF.FF.FF.FF的IP地址是2 ^ 32,并且不能大于该数字。请参阅:

This stack overflow article on the same subject

要将该数字转回IP地址,您必须将数字分解为4个部分,因为每个字节是地址的一个八位字节,因此将数字转换为十六进制,然后解析每对。您可能需要或不必为第一个八位字节添加前导零。

此外,您可能必须处理整数的字节顺序(endien问题),但由于现在大多数系统都是基于英特尔的,因此您可能不需要处理它。

答案 6 :(得分:0)

var aaa = Number("0b"+ "192.168.2.44".split(".").map(
  function(dec){
    return ("00000000" + Number(dec).toString(2)).slice(-8); 
  }).join(""));

aaa.toString(2).match(/.{1,8}/g).map(
  function(bin){
    return Number("0b"+bin); 
  }).join(".");

答案 7 :(得分:0)

我修改了Evan的最终答案,特别是dot2num。它的功能相同,但可能更易读,速度稍慢。

function ip2num(ip) {
    var parts = ip.split('.');

    var num = 0;
    num += d[0] * Math.pow(2, 24);
    num += d[1] * Math.pow(2, 16);
    num += d[2] * Math.pow(2, 8);
    num += d[3];

    return num;
}

function num2ip(num) {
    var ip = num % 256;

    for (var i=3; i > 0; i--) { 
        num = Math.floor(num / 256);
        ip = num % 256 + '.' + ip;
    }

    return ip;
}

答案 8 :(得分:0)

试试这个解决方案,它可能会有所帮助:

function IpToInteger(ipAddr)
{
    var parts = ipAddr.split('.');
    return (((parts[0] ? parts[0] << 24 : 0) |
             (parts[1] ? parts[1] << 16 : 0) |
             (parts[2] ? parts[2] << 8  : 0) |
             (parts[3])) >>> 0);
}

答案 9 :(得分:0)

function IpAddressToLong(ip){
  return ip.split('.').map((octet, index, array) => {
      return parseInt(octet) * Math.pow(256, (array.length - index - 1));
    }).reduce((prev, curr) => {
      return prev + curr;
  });
}

取自repo

答案 10 :(得分:0)

const ip2int = (x) => (x.split('.').reduce((a, v) => ((a << 8) + (+v)), 0) >>> 0);

答案 11 :(得分:0)

使用这个

function num2string(ip) {
    return [24,16,8,0].map(n => (ip >> n) & 0xff).join(".")
}
function string2num(ip) {
    return ip.split(".").reduce((sum,x,i) => sum + (x << 8*(3-i)), 0)
}