Вопрос: Как принудительно закрыть сокет в TIME_WAIT?


Я запускаю определенную программу на Linux, которая иногда сбой. Если вы откроете его быстро после этого, он прослушивает гнездо 49201 вместо 49200, как это делалось в первый раз. netstat показывает, что 49200 находится в состоянии TIME_WAIT.

Есть ли программа, которую вы можете запустить, чтобы немедленно вывести этот сокет из состояния TIME_WAIT?


109
2017-09-03 12:57


Источник


Если вы здесь из-за "Очень много TIME_WAIT на сервере ", просто пропустить первые три ответа, которые позволяют избежать вопроса, а не отвечать на него. - Pacerier


Ответы:


/etc/init.d/networking restart

Позвольте мне уточнить. Протокол управления передачей (TCP) предназначен для двунаправленного, упорядоченного и надежного протокола передачи данных между двумя конечными точками (программами). В этом контексте термин «надежный» означает, что он будет повторно передавать пакеты, если он потеряется посередине. TCP гарантирует надежность, отправив обратно пакеты подтверждения (ACK) обратно для одного или нескольких пакетов, полученных от однорангового узла.

Это одинаково для управляющих сигналов, таких как запрос / ответ на запрос. RFC 793 определяет состояние TIME-WAIT следующим образом:

TIME-WAIT - означает ожидание   достаточно времени, чтобы пройти, чтобы быть уверенным       удаленный TCP получил подтверждение своего соединения       запрос завершения.

См. Следующую схему состояния TCP: alt text

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

Назовем первое, чтобы вызвать завершение работы как более активное, а другое - ближе к пассивному. Когда активный ближе посылает FIN, состояние переходит к FIN-WAIT-1. Затем он получает ACK для отправленного FIN, и состояние переходит к FIN-WAIT-2. Как только он получает FIN также от пассивного ближе, активный ближе посылает ACK в FIN, и состояние переходит к TIME-WAIT. В случае, если пассивный ближе не получил ACK ко второму FIN, он будет повторно передавать пакет FIN.

RFC 793 устанавливает TIME-OUT в два раза больше максимального срока службы сегмента или 2MSL. Поскольку MSL, максимальное время, когда пакет может перемещаться по Интернету, устанавливается в 2 минуты, 2MSL - 4 минуты. Поскольку ACK для ACK отсутствует, активный ближе не может ничего сделать, кроме как ждать 4 минут, если он правильно соблюдает протокол TCP / IP, на случай, если пассивный отправитель не получил ACK до своего FIN (теоретически) ,

На самом деле недостающие пакеты, вероятно, редки и очень редки, если все это происходит в локальной сети или на одной машине.

Чтобы ответить на дословный вопрос, принудительно закройте сокет в TIME_WAIT ?, я все равно буду придерживаться своего первоначального ответа:

/etc/init.d/networking restart

Фактически, я бы запрограммировал его, чтобы он игнорировал состояние TIME-WAIT с использованием опции SO_REUSEADDR, как упоминалось в WMR. Что именно делает SO_REUSEADDR?

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


139
2017-09-03 13:11



Отличный ответ, но не правильный ответ на его вопрос. Перезапуск сети будет работать, но потом перезагрузится, так что это не может быть правильно. - Chris Huang-Leaver
@Chris Huang-Leaver, вопрос: «Есть ли программа, которую вы можете запустить, чтобы немедленно заставить этот сокет выйти из состояния TIME_WAIT?» если перезагрузка может считаться запуском программы, то это тоже будет правильным ответом. Почему вы думаете, что это не так? - Eugene Yokota
У WMR есть самый полезный ответ (это то, что я делаю, когда сталкиваюсь с такой проблемой). Перезапуск сети слишком радикален, чтобы быть решением, и может занять больше времени, чем просто ждать тайм-аута. Правильный ответ на его вопрос - «Нет», но SO не позволит вам печатать два письма :-) - Chris Huang-Leaver
О, хорошо, в следующий раз, когда какой-нибудь процесс зависает на SIGTERM, я просто разбил свой компьютер, а не исправил его. - Longpoke


Я не знаю, есть ли у вас исходный код той конкретной программы, в которой вы работаете, но если это так, вы можете просто установить SO_REUSEADDR через setsockopt(2) который позволяет вам связываться на том же локальном адресе, даже если сокет находится в состоянии TIME_WAIT (если этот сокет не прослушивается активно, см. socket(7)).

Для получения дополнительной информации о состоянии TIME_WAIT см. Часто задаваемые вопросы по Unix,


50
2017-09-03 13:17



но я не получил уже связанную ошибку. когда я снова запускаю программу, она прослушивает сообщение (123456), также я вижу, что система показывает TIME_WAIT для этого порта, но все же я могу подключиться. Зачем? - Jayapal Chandran
Даже с SO_REUSEADDR все еще можно получить ошибку «Адрес уже используется». Для получения дополнительной информации см. hea-www.harvard.edu/~fine/Tech/addrinuse.html, - Jingguo Yao
@WMR SO_REUSEADDR не «закрывает» сокет. Он просто позволяет вам повторно использовать те, которые уже открыты. Таким образом, остается вопрос: «Как принудительно закрыть сокет в TIME_WAIT?» - Pacerier


Насколько я знаю, нет возможности принудительно закрыть сокет за пределами написания лучшего обработчика сигнала в вашей программе, но есть файл / proc, который определяет, сколько времени занимает тайм-аут. Файл

/proc/sys/net/ipv4/tcp_tw_recycle

и вы можете установить тайм-аут на 1 секунду, выполнив следующее:

echo 1 > /proc/sys/net/ipv4/tcp_tw_recycle 

Однако, эта страница содержит предупреждение о возможных проблемах с безопасностью при настройке этой переменной.

Существует также связанный файл

/proc/sys/net/ipv4/tcp_tw_reuse

который контролирует возможность повторного использования сокетов TIME_WAIT (предположительно без какого-либо таймаута).

Кстати, документация ядра предупреждает вас не изменять ни одно из этих значений без «советов / запросов технических экспертов». Который я нет.

Программа должна быть написана для попытки привязки к порту 49200, а затем увеличения на 1, если порт уже используется. Поэтому, если у вас есть контроль над исходным кодом, вы можете изменить это поведение, чтобы подождать несколько секунд и повторите попытку на том же порту, вместо того, чтобы увеличивать его.


32
2017-09-03 13:24



подумайте, что во втором примере должны быть s / rw / tw /, я бы отредактировал, но не хватило репутации.
Из документации ядра: Внимание. И tcp_tw_recycle, и tcp_tw_reuse могут вызвать проблемы. Вы не должны включать либо без понимания топологии сети между узлами (узлами), которые используются или используются узлом, где параметр включен. Соединения, которые идут через узлы, которые знают о состояниях TCP-соединения, таких как брандмауэр, NAT или балансировщик нагрузки, могут начать отбрасывать кадры из-за настройки. Проблема будет заметна, когда будет достаточно большое количество соединений.
Установка 1 работает для будущих подключений, но как насчет тех текущих, которые уже открыты? - Pacerier


На самом деле есть способ убить соединение - killcx, Они утверждают, что он работает в любом состоянии связи (которое я еще не подтвердил). Вам нужно знать интерфейс, где происходит связь, но по умолчанию он принимает eth0.

ОБНОВЛЕНИЕ: другое решение резак который входит в некоторые хранилища linux-дистрибутивов.


16
2017-10-30 17:32



Благодаря! Эта утилита отлично работает! Уберегли меня от необходимости возобновить долгую работу. - Zanson


Другой вариант - использовать опцию SO_LINGER с таймаутом 0. Таким образом, когда вы закрываете сокет, принудительно закрывается, отправляя RST вместо перехода в режим закрытия FIN / ACK. Это позволит избежать состояния TIME_WAIT и может быть более подходящим для некоторых видов использования.


3
2018-06-10 22:33



Он также теряет все исходящие данные, которые все еще находятся в пути, и может вызвать ошибку на другом конце. Не рекомендуется. - user207421
@EJP Неудачное раннее - почти всегда правильный звонок. Сеть не является надежной, и борьба с ней замедлит работу. Разбитое приложение не может предположить, что любые данные сделали это безопасно. - Tobu
На самом деле, я бы рекомендовал это в любой день, когда другой конечной точкой является баггированный встроенный шлюз промышленной шины, который реализует собственную надёжную транспортную среду на уровне приложения на основе TCP, где указанный транспорт предотвращает закрытие соединения, если он не получает RST и, таким образом, заполняет лимит подключения на этом шлюзе. Там. Я дал вам очень конкретный и очень реальный пример, который, к сожалению, требует прибегать к таким хакам. - andyn
@Tobu Networking не является надежным, но TCP пытается быть, и ухудшение ситуации не создает ничего лучшего и позволяет TCP выполнять свою работу, а не «сражаться». - user207421


Альтернативным решением было бы иметь некоторое надежное программное обеспечение для перенаправления прокси или порта, которое прослушивает порт 49200, а затем перенаправляет соединение на один из нескольких экземпляров вашей менее надежной программы, используя разные порты ... HAPROXY помнят.

Кстати, порт, на котором вы подключаетесь, довольно высок. Вы можете попробовать использовать неиспользуемый только выше диапазона 0-1024. Ваша система с меньшей вероятностью использует более низкий номер порта в качестве эфемерного порта.


2
2017-08-21 20:28





TIME_WAIT является наиболее распространенной проблемой в архитектуре клиентского сервера сокетов. Подождите несколько секунд, пытаясь периодически, это лучшее решение для него. Для приложений реального времени им нужно, чтобы сервер немедленно вставал Для них есть опция SO_REUSEADDR.


0
2017-10-13 19:07