Я открываю тысячи сокетов, и иногда программа вылетает, оставляя мне гораздо меньше доступных сокетов. Есть ли способ почистить эти висящие розетки?
1 ответ
Сокеты окончательно закрываются ядром Unix; сбойная программа ничем не отличается от обычного выхода из программы без вызова close()/shutdown()
.
Возможно, ваша проблема связана с состоянием TIME_WAIT конечного автомата TCP/IP и должна быть решена с помощью опции SO_REUSEADDR. Один из способов подтвердить это - подождать около 5 минут, прежде чем снова начинать работу после сбоя. Если вы обнаружите, что в данный момент доступно достаточно сокетов, вам следует изучить логику TIME_WAIT и обойти ее. Если трюк ожидания не решит вашу проблему, возможно, в вашей программе есть другая проблема, которую необходимо определить.
Вот хорошее чтение на эту тему,
TIME_WAIT и его влияние на дизайн протоколов и масштабируемых систем клиент-сервер
Две быстрые выдержки оттуда для справки,
TIME_WAIT часто также называют состоянием ожидания 2MSL. Это связано с тем, что сокет, который переходит к TIME_WAIT, остается там в течение периода, который в 2 раза превышает максимальное время жизни сегмента. MSL - это максимальный период времени, в течение которого любой сегмент, для всех намерений и целей дейтаграммы, которая является частью протокола TCP, может оставаться действительным в сети до того, как будет отброшен. Этот предел времени в конечном счете ограничен полем TTL в дейтаграмме IP, которая используется для передачи сегмента TCP. Различные реализации выбирают разные значения для MSL, и общие значения составляют 30 секунд, 1 минуту или 2 минуты. RFC 793 указывает MSL как 2 минуты, и системы Windows по умолчанию используют это значение, но его можно настроить с помощью параметра реестра TcpTimedWaitDelay.
(PS: следовательно, 4+1
минута ожидания для моего теста, предложенного выше)
Изменение задержки 2MSL обычно является изменением конфигурации всей машины. Вместо этого вы можете попытаться обойти TIME_WAIT на уровне сокета с помощью опции сокета SO_REUSEADDR. Это позволяет создать сокет, в то время как существующий сокет с тем же адресом и портом уже существует. Новый сокет, по сути, захватывает старый сокет. Вы можете использовать SO_REUSEADDR, чтобы разрешить создание сокетов, в то время как сокет с тем же портом уже находится в TIME_WAIT, но это также может вызвать проблемы, такие как атаки типа «отказ в обслуживании» или кража данных.
В статье описан еще один способ. Но это приходит с другими оговорками.
Есть еще один способ разрыва TCP-соединения, который прерывает соединение и отправляет RST, а не FIN. Обычно это достигается установкой параметра сокета SO_LINGER на 0. Это приводит к тому, что ожидающие данные отбрасываются и соединение прерывается с помощью RST, а не для ожидающих данных, которые должны передаваться, и соединение закрывается чисто с помощью FIN. Важно понимать, что, когда соединение прерывается, любые данные, которые могут быть в потоке между узлами, отбрасываются, и RST доставляется немедленно; обычно как ошибка, которая представляет факт, что "соединение было сброшено узлом". Удаленный узел знает, что соединение было прервано, и ни один узел не входит в TIME_WAIT.
Перед использованием этих схем рекомендуется понять поведение TCP-машины, чтобы вы случайно не представили другие ситуации, которые позже понадобятся для отладки. Так что хотя бы прочитайте эту статью полностью :-)