IP-адреса, сохраненные как int, приводят к переполнению?

Я пишу чат-сервер в 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?

Ответы

Ответ 1

Почему преобразованный IP-адрес отрицательный?

Это НЕ переполнение. Первая часть вашего IP-адреса - 192, которая преобразуется в 11000000 в двоичном формате. Затем вы перемещаете все это влево. Когда в левом крайнем положении находится 32-битное число, это отрицательное значение.

Как преобразовать обратно в строку?

Сделайте то же самое, что и вы, чтобы преобразовать из строки, но наоборот. Сдвиг вправо (и маска)!

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

Ответ 2

Результат "< < оператор всегда представляет собой подписанное 32-битное целое число, согласно спецификации.

Когда вы переходите назад, используйте " → > ", чтобы выполнить сдвиг вправо без знака.

Ответ 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 с этим.

Ответ 4

Вы смещены влево, чтобы получить исходный номер - это всего 4 набора битов независимо от знака.

Сдвиньте право, чтобы вернуться к IP. Не имеет значения, что такое знак.

Ответ 5

IP-адреса в пространстве V4 представляют собой неподписанные 32-битные номера, поэтому IP-адрес FF.FF.FF.FF равен 2 ^ 32 и не может быть больше этого числа. Пожалуйста, смотрите:

Эта статья по тому же вопросу

Чтобы вернуть это число в IP-адрес, вы должны разбить его на 4 части, поскольку каждый байт представляет собой один октет адреса, чтобы преобразовать число в шестнадцатеричный, а затем разобрать каждую пару. Возможно, вам может понадобиться добавить начальный ноль для первого октета.

Кроме того, может иметь дело с порядком байтов целого числа (проблемы с эндиеном), но поскольку большинство систем основаны на интеллекте в наши дни, вам, возможно, не придется справляться с этим.

Ответ 6

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

Я пересмотрел окончательный ответ Эвана немного, особенно 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

Попробуйте это решение, это может помочь:

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