Вопрос
Для моего серверного приложения мне нужно проверить, находится ли IP-адрес в нашем черном списке.
Какой наиболее эффективный способ сравнения IP-адресов?Будет ли преобразование IP-адреса в целое число и их сравнение эффективным?
Решение
Зависит от того, какой язык вы используете, но IP-адрес обычно хранится как 32-битное целое число без знака, по крайней мере, на сетевом уровне, что делает сравнения довольно быстрыми.Даже если это не так, если только вы не разрабатываете высокопроизводительное приложение с коммутацией пакетов, это вряд ли станет узким местом в производительности.Избегайте преждевременной оптимизации — создавайте свою программу так, чтобы ее можно было тестировать и масштабировать, а если у вас есть проблемы с производительностью, вы можете использовать профилировщик, чтобы увидеть, где находятся узкие места.
Редактировать:чтобы уточнить, адреса IPv4 хранятся как 32-битные целые числа плюс сетевая маска (которая не требуется для сравнения IP-адресов).Если вы используете более новый и в настоящее время более редкий IPv6, длина адреса будет 128 бит.
Другие советы
32-битные целые числа — это то, что вам нужно, пока вы не начнете иметь дело со 128-битными адресами IPv6.
Вы имеете в виду, следует ли вам сравнить его как текстовую строку или преобразовать int в int и сравнить как int?
Обычно это не является узким местом в такого рода поиске.вы можете просто попробовать реализовать оба метода и посмотреть, какой из них работает быстрее.
Настоящая проблема при поиске IP-адресов обычно заключается в создании эффективных запросов с использованием того факта, что вы имеете дело с IP-адресами, а не просто со случайными числами.для этого вы можете поискать LC трие и возможно Эта статья
Очевидно, это должно вас заинтересовать, только если ваш черный список содержит десятки тысяч или миллионы записей.Если в нем всего 10-20 записей, следует отдать предпочтение линейному поиску, и действительно, более интересным вопросом является сравнение текста и целочисленного сравнения.
static public bool IsEqual(string ToCompare,
string CompareAgainst)
{
return IPAddressToLongBackwards(ToCompare)==IPAddressToLongBackwards(CompareAgainst);
}
static private uint IPAddressToLongBackwards(string IPAddr)
{
System.Net.IPAddress oIP=System.Net.IPAddress.Parse(IPAddr);
byte[] byteIP=oIP.GetAddressBytes();
uint ip=(uint)byteIP[0]<<24;
ip+=(uint)byteIP[1]<<16;
ip+=(uint)byteIP[2]<<8;
ip+=(uint)byteIP[3];
return ip;
}
Если я правильно вас понял, это код для сравнения двух IP-адресов.Вы хотите это?Далее вы можете делать такие вещи, как:
static public bool IsGreater(string ToCompare,
string CompareAgainst)
{
return IPAddressToLongBackwards(ToCompare)>
IPAddressToLongBackwards(CompareAgainst);
}
потому что вы получили байты адреса.
Да, я обнаружил, что для эффективности это займет много времени, и, конечно, вам придется индексировать IP-адреса, занесенные в черный список, в целочисленной форме.
Используйте такой инструмент, как PeerGuardian, который запрещает входящие TCP/IP-соединения на уровне драйвера к IP-адресам из черного списка.Высокая безопасность, код не требуется (возможно:очень безопасный, потому что код не требуется).
Я сделал это и протестировал, используя беззнаковое целое число (32 бита) является самым быстрым - я предполагаю, что вы сравниваете это со строковым представлением.
Еще одна вещь, которая может вам помочь: при создании таблицы раньше у меня было 2 столбца:LowIP и HighIP;Таким образом, я смог занести в черный список целые диапазоны IP-адресов с одной записью и при этом получить хорошую производительность, проверяя IP-адреса в диапазоне.
Однажды я унаследовал код, в котором кто-то мысль что хранить IP-адреса в виде 4 целых чисел было действительно хорошо, за исключением того, что они тратили все свое время на преобразование в/из целых чисел.
Хранить их в виде строк в базе данных было гораздо проще, и для этого требовался всего один индекс.Вы будете удивлены, насколько хорошо SQL-сервер может индексировать строки, а не 4 столбца целых чисел.Но этот список IP не предназначен для внесения в черный список.Обращение к базе данных туда и обратно обходится довольно дорого.
Если база данных перегружена, сохраните их в словаре в памяти, но это всего лишь предположение, поскольку мы понятия не имеем, сколько вам нужно сравнить.Поскольку большинство хеш-кодов представляют собой 32-битные целые числа, а адреса IPv4 — 32-битные, сам IP-адрес может быть просто хорошим хэш-кодом.
Но, как отмечают другие, лучшим вариантом может быть снижение нагрузки на ваш сервер и покупка специализированного оборудования.Возможно, вы храните в памяти недавно занесенные в черный список IP-адреса и периодически публикуете новые на маршрутизаторе.
Если вы пытаетесь создать какое-то программное обеспечение внутри маршрутизатора, вам нужно будет выудить книгу о структурах данных и создать что-то вроде b-дерева.
Оптимальной структурой для этого является дерево Radix или PATRICIA.
Ознакомьтесь с исходным кодом C для инструментов потока:http://www.splintered.net/sw/flow-tools/
Я работал над этим много лет назад.
Есть ли у вас проблемы с эффективностью?
Если да, то обязательно опубликуйте код (или псевдокод), и мы сможем ковыряться в трупе.
Если нет, то я бы предложил попробовать что-то простое, например сохранить записи в отсортированном списке и использовать существующую среду вашей среды. Sort()
и Find()
.
Сравнение целых чисел выполняется намного быстрее, чем сравнение строк.
Если вы сохраните целые числа в отсортированном списке, вы сможете найти их быстрее, чем в несортированном списке.
если вы получаете IP-адрес в виде строки, сравнение его со строкой может быть более эффективным, чем преобразование его в целочисленное представление.
но я бы профилировал оба решения, чтобы быть уверенным, если несколько миллисекунд (наносекунд!) будут иметь значение для этой операции ;-)