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

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

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

109 голосов | спросил Rehan Khwaja 3 rdEurope/Moscowp30Europe/Moscow09bEurope/MoscowWed, 03 Sep 2008 16:57:26 +0400 2008, 16:57:26

7 ответов


139
/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), идите вперед и   повторное использование. Если он занят, но   с другим государством вы все равно получите   адрес, уже использующий ошибку. Это   полезно, если ваш сервер закрыт   вниз, а затем сразу же перезапущен   в то время как сокеты все еще активны на   порт. Вы должны знать, что если   любые непредвиденные данные поступают, это может   путайте свой сервер, но пока это   возможно, это маловероятно.

ответил Eugene Yokota 3 rdEurope/Moscowp30Europe/Moscow09bEurope/MoscowWed, 03 Sep 2008 17:11:37 +0400 2008, 17:11:37
50

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

Для получения дополнительной информации о состоянии TIME_WAIT см. FAQ о сокетах Unix .

ответил WMR 3 rdEurope/Moscowp30Europe/Moscow09bEurope/MoscowWed, 03 Sep 2008 17:17:07 +0400 2008, 17:17:07
32

Насколько я знаю, нет способа принудительно закрыть сокет за пределами написания лучшего обработчика сигнала в вашей программе, но есть файл /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, если порт уже используется. Поэтому, если вы контролируете исходный код, вы можете изменить это поведение, чтобы подождать несколько секунд и повторите попытку на том же порту, вместо того, чтобы увеличивать.

ответил Leigh Caldwell 3 rdEurope/Moscowp30Europe/Moscow09bEurope/MoscowWed, 03 Sep 2008 17:24:42 +0400 2008, 17:24:42
16

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

UPDATE: другое решение - резак , который входит в некоторые репозитории linux-дистрибутивов.

ответил akostadinov 30 +04002011-10-30T21:32:47+04:00312011bEurope/MoscowSun, 30 Oct 2011 21:32:47 +0400 2011, 21:32:47
3

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

ответил 11 J0000006Europe/Moscow 2010, 02:33:36
2

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

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

ответил andrew pate 22 AM000000120000003031 2014, 00:28:30
0

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

ответил 13 +04002011-10-13T23:07:51+04:00312011bEurope/MoscowThu, 13 Oct 2011 23:07:51 +0400 2011, 23:07:51

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132