ip to int и int to ip на пальцах
UPD 2023.12.15 👋
Заканчивается 2023 год, а статья до сих пор хорошая. Эта тема до сих пор находит отголоски в моих текущ их задачах. Именно изучение того, как конвертируется ip толкнуло меня в понимании побитовых операций, а также сетевых масок.
Мне потребовалась функция, определяющая, находится ли IP под указанной маской и я захотел в деталях понять, как она работает. Для этого надо было понять, как IP конвертируются в целые числа и наоборот
Преобразования IP в число и обратно это побитовые операции, но я постараюсь объяснить все так, чтобы понятно было тем, кто с ними не знаком или плохо знаком. Подробнее о них можно почитать, например, здесь.
Представление IP
Как вам известно, IP состоит из 4 блоков, где число в блоке <= 255. Почему? В старые времена компьютеры поддерживали числа только до 32 бит. В 32 битах максимальное число 2^32-1 = 4294967295
. В шестнадцатиричной системе (HEX) оно выглядит как FFFFFFFF или 32 ноля в двоичной (BIN).
А теперь разделим FFFFFFFF на 4 кусочка (блока): FF FF FF FF
FF HEX = 255 DEC
. Отсюда и получается максимально большой ИП: 255.255.255.255.
IP это HEX или BIN кусочки, разделенные точкой
Вид 255.255.255.255 в разных представлениях:
DEC 255 255 255 255 BIN 11111111 11111111 11111111 11111111 HEX FF FF FF FF
Ноли в начале чисел ничего не значат. Тоесть, 000001 == 1, поэтому если в тексте ниже сначала чисел будут ноли, то они добавлены "для красоты"
Еще пример. Возьмем любой другой ИП, например 37.230.210.51. Разделим его на кусочки: 37 230 210 51. Конвертируем каждый из этих кусочков в HEX и BIN:
DEC 037 230 210 051 BIN 00100101 11100110 11010010 00110011 HEX 25 E6 D2 33
Конвертер десятичных числел в двоичные и HEX здесь
Склеиваем, получается HEX 25E6D233
или BIN 00100101 11100110 11010010 00110011
. Теперь можете конвертировать это в DEC и получите IP в виде числа, которым его удобно хранить в БД
Функция конвертации IP в число
local function ipToInt(ip) -- ip = 37.230.210.51
local int = 0
-- 31 230 210 51
local p1, p2, p3, p4 = ip:match("(%d+)%.(%d+)%.(%d+)%.(%d+)")
-- BIN | HEX
int = int + bit.lshift(p1,24) -- 00100101 00000000 00000000 00000000 | 25 00 00 00
int = int + bit.lshift(p2,16) -- 00100101 11100110 00000000 00000000 | 25 E6 00 00
int = int + bit.lshift(p3,8) -- 00100101 11100110 11010010 00000000 | 25 E6 D2 00
int = int + p4 -- 00100101 11100110 11010010 00110011 | 25 E6 D2 33
return int -- 635884083
end
Функция bit.lshift(a, b)
смещает число a
на b
бит влево (Добавляя ноли справа).
Тоесть 11010111
после смещения на 3 бита влево превратится в 11010111000
. После этого смещение на 3 бита вправо вернет число в прежнее состояние, убрав ноли справа
Что происходит в функции? Она помещает каждый кусочек IP на свое место в битовом представлении и на выхлопе получается нужное число. Функция делает то же самое, что было описано ранее, только за счет математики
Конвертация числа в IP
Итак, у нас есть IP в виде DEC числа и нам нужно сделать из него глазу понятный IP. Алгоритм человеческим языком:
- Представляем DEC в виде BIN
- Вырываем с этого BIN 4 кусочка
- Разделяем их точкой
Lua функция:
-- Маски из примера ниже
-- FF 00 00 00 == 11111111 00000000 00000000 00000000
-- 00 FF 00 00 == 00000000 11111111 00000000 00000000
-- 00 00 FF 00 == 00000000 00000000 11111111 00000000
-- 00 00 00 FF == 00000000 00000000 00000000 11111111
local function intToIp(int) -- 635884083 (00100101 11100110 11010010 00110011)
local a = bit.band(int, 0xFF000000) -- 00100101 00000000 00000000 00000000 | 25 00 00 00 | 037.000.000.000
local b = bit.band(int, 0x00FF0000) -- 00000000 11100110 00000000 00000000 | 00 E6 00 00 | 000.230.000.000
local c = bit.band(int, 0x0000FF00) -- 00000000 00000000 11010010 00000000 | 00 00 D2 00 | 000.000.210.000
local d = bit.band(int, 0x000000FF) -- 00000000 00000000 00000000 00110011 | 00 00 00 33 | 000.000.000.051
a = bit.rshift(a, 24) -- помещаем в самое начало
b = bit.rshift(b, 16) -- на второе место
c = bit.rshift(c, 8) -- на третье
-- четвертое уже на своем
local ip = a .. "." .. b .. "." .. c .. "." .. d
return ip -- 37.230.210.51
end
print( intToIp(635884083) )
Функция bit.band(a, b) заменяет побитовый оператор AND (и). Он возвращает число, в котором только те биты, которые установлены и в a
, и в b
. Тоесть bit.band(0x00FF, 0xFF00)
вернет 0xFFFF. Это эквивалентно bit.band(0000000011111111, 1111111100000000) == 1111111111111111
.
rshift это противоположность lshift, о котором я писал выше
Бонус
Я начал изучение этого вопроса с функции, говорящей, входит ли IP в маску, поэтому делюсь и ею: