Я запускаю определенную программу на Linux, которая иногда дает сбой. Если после этого вы откроете его быстро, он будет прослушивать сокет 49201 вместо 49200, как в первый раз. netstat показывает, что 49200 находится в состоянии TIME_WAIT.
Есть ли программа, которую вы можете запустить, чтобы немедленно вывести этот сокет из состояния TIME_WAIT?
/etc/init.d/networking restart
Позвольте мне уточнить. Протокол управления передачей (TCP) разработан как двунаправленный, упорядоченный и надежный протокол передачи данных между двумя конечными точками (программами). В этом контексте термин надежный означает, что он будет повторно передавать пакеты, если они будут потеряны в середине. TCP гарантирует надежность, отправляя обратно пакеты подтверждения (ACK) для одного или нескольких пакетов, полученных от однорангового узла.
То же самое касается сигналов управления, таких как запрос / ответ на завершение. RFC 793 определяет состояние ВРЕМЯ-ОЖИДАНИЕ следующим образом:
TIME-WAIT - означает ожидание, пока пройдет достаточно времени, чтобы убедиться, что удаленный TCP получил подтверждение своего запроса на завершение соединения.
См. Следующую диаграмму состояний TCP:
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), продолжайте и используйте его повторно. Если он занят, но в другом состоянии, вы все равно получите ошибку адреса, который уже используется. Это полезно, если ваш сервер был выключен, а затем сразу перезапущен, пока сокеты на его порте все еще активны. Вы должны знать, что если приходят какие-либо неожиданные данные, они могут сбить с толку ваш сервер, но, хотя это возможно, это маловероятно.
Я не знаю, есть ли у вас исходный код этой конкретной программы, но если да, вы можете просто установить SO_REUSEADDR через setsockopt(2)
который позволяет вам выполнять привязку к одному и тому же локальному адресу, даже если сокет находится в состоянии TIME_WAIT (если этот сокет активно не прослушивает, см. socket(7)
).
Для получения дополнительной информации о состоянии TIME_WAIT см. Unix socket FAQ.
Насколько мне известно, невозможно принудительно закрыть сокет, кроме написания лучшего обработчика сигналов в вашей программе, но есть файл / 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, если порт уже используется. Поэтому, если у вас есть контроль над исходным кодом, вы можете изменить это поведение, чтобы подождать несколько секунд и повторить попытку на том же порте вместо увеличения.
На самом деле есть способ убить соединение - killcx. Они утверждают, что он работает в любом состоянии соединения (что я не проверял). Вам нужно знать интерфейс, через который происходит коммуникация, по-видимому, по умолчанию используется eth0.
ОБНОВЛЕНИЕ: другое решение резак который входит в репозитории некоторых дистрибутивов Linux.
Другой вариант - использовать параметр SO_LINGER с таймаутом 0. Таким образом, при закрытии сокет принудительно закрывается, отправляя RST вместо перехода в режим закрытия FIN / ACK. Это позволит избежать состояния TIME_WAIT и может быть более подходящим для некоторых целей.
Альтернативным решением может быть надежный прокси-сервер или программное обеспечение для переадресации портов, которое прослушивает порт 49200, а затем перенаправляет соединение на один из нескольких экземпляров вашей менее надежной программы, используя разные порты ... На ум приходит HAPROXY.
Между прочим, порт, к которому вы подключаетесь, довольно высок. Вы можете попробовать использовать неиспользованный чуть выше диапазона 0-1024. Ваша система с меньшей вероятностью будет использовать меньший номер порта в качестве временного порта.
TIME_WAIT - наиболее распространенная проблема в архитектуре клиент-сервера программирования сокетов. Подождите несколько секунд, периодически пытаясь решить эту проблему. Для приложений реального времени им нужен сервер должен немедленно встать. Для них есть опция SO_REUSEADDR.