【问题标题】:IP-addresses stored as int results in overflow?存储为 int 的 IP 地址会导致溢出?
【发布时间】:2011-11-12 16:08:17
【问题描述】:

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

这是我的代码:

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?

【问题讨论】:

  • 您的整个 ipToInt 函数可以替换为:new Buffer(ip.split('.')).readInt32BE(0)
  • 关于 Nikolai 的解决方案 - 在某些情况下,这会抛出 RangeError('尝试访问超出缓冲区长度')

标签: javascript


【解决方案1】:

为什么转换后的 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;
}

为什么要重新发明轮子?来自谷歌:

或者,您可以使用我在此处找到的内容:
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;
}

【讨论】:

  • 非常感谢,这很有帮助!我认为 javascript 总是使用 64 位有符号浮点数,但正如您所说,移位是在转换为 32 位有符号整数后执行的。那是我错了!
  • @JPuge 不客气!大多数语言尽可能避免使用浮点数。它们非常不精确。它们还需要一种完全不同类型的处理器(浮点处理器),其中 int 只使用加法器/多路复用器和其他非常简单的东西。
【解决方案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 实用功能。

    【讨论】:

    • ip.toLong 可能不起作用,因为在执行 forEach 循环中的函数之前返回了值。只需使用普通的 for(int i=0, ...)...
    【解决方案4】:

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

    右移返回 IP。不管标志是什么。

    【讨论】:

    • ((192&lt;&lt;24)&gt;&gt;24) !== 192 所以我不确定它是否可靠。
    • 您需要做一个位掩码,因为移位会扩展符号。 (((192> 24) & 255) 应该等于 192。
    • @pimvdb @evan 或使用 Bitwise Unsigned Right Shift - 运算符 &gt;&gt;&gt; 已存在多年:((192&lt;&lt;24)&gt;&gt;&gt;24) === 192
    【解决方案5】:
    const ip2int = (x) => (x.split('.').reduce((a, v) => ((a << 8) + (+v)), 0) >>> 0);
    

    【讨论】:

      【解决方案6】:

      单线:

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

      【讨论】:

        【解决方案7】:

        使用这个

        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)
        }
        

        【讨论】:

          【解决方案8】:

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

          This stack overflow article on the same subject

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

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

          【讨论】:

          • "V4 中的 IP 是 32 位" == "2^32" == 32 位数。
          • 有趣的评论,这不是我说的吗?
          • 嗯...我一定误解了你在说什么。不知道语句末尾的“那个数字”指的是什么。当我读到的内容与您的意思不同时,我可能已经“填写”了。
          【解决方案9】:
          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(".");
          

          【讨论】:

            【解决方案10】:

            我稍微修改了 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;
            }
            

            【讨论】:

              【解决方案11】:

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

              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);
              }
              

              【讨论】:

                【解决方案12】:
                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

                【讨论】:

                  【解决方案13】:

                  function ip2num(ip) {
                    var d = ip.split(".");
                  
                    var num = 0;
                    num += Number(d[0]) * Math.pow(256, 3);
                    num += Number(d[1]) * Math.pow(256, 2);
                    num += Number(d[2]) * Math.pow(256, 1);
                    num += Number(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;
                  }
                  
                  console.log(ip2num("192.168.0.1"));
                  console.log(num2ip(3232235521))
                  &lt;h1&gt;YOU IS WELCOME&lt;/h1&gt;

                  【讨论】:

                    猜你喜欢
                    • 1970-01-01
                    • 2019-06-09
                    • 2011-04-15
                    • 1970-01-01
                    • 2021-07-02
                    • 1970-01-01
                    • 2018-06-21
                    • 2019-05-20
                    • 1970-01-01
                    相关资源
                    最近更新 更多