Как закрыть сокет linux
Перейти к содержимому

Как закрыть сокет linux

  • автор:

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

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

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

Ответ 1

/etc/init.d/networking restart

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

То же самое относится и к управляющим сигналам, таким как запрос/ответ на завершение. RFC793 определяет состояние TIME-WAIT следующим образом:

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

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

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

RFC793 устанавливает 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. Что именно делает SO_REUSEADDR?

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

Ответ 2

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

Ответ 3

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

Ответ 4

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

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

Мы будем очень благодарны

если под понравившемся материалом Вы нажмёте одну из кнопок социальных сетей и поделитесь с друзьями.

Как закрыть сокет (TCP) полностью?

сокет долгое время остается в состоянии FIN_WAIT2 (смотрю в TcpView)
Возможно ли его закрыть полностью?

  • Вопрос задан более трёх лет назад
  • 2745 просмотров

Комментировать
Решения вопроса 1

Устанавливайте setsockopt() опцию SO_LINGER с нулевым или маленьким таймаутом перед закрытием сокета.

Ответ написан более трёх лет назад
Нравится 2 6 комментариев
ChymeNik @ChymeNik Автор вопроса

LINGER opt;
opt.l_onoff = 1;
opt.l_linger = 0;
setsockopt(tcp->sockfd, SOL_SOCKET, SO_LINGER, &opt, sizeof opt);
shutdown(tcp->sockfd, SHUT_RDWR);
close(tcp->sockfd);

Как и без опции SO_LINGER, сокеты остаются в FIN_WAIT2 около 2-х минут даже после полного закрытия программы

Если у вас BSD, то, скорей всего, это не лечится, чтобы избежать проблем при повторном запуске демона/открытии порта используйте SO_REUSEADDR / SO_REUSEPORT. Если Linux, то это странно, потому что при нулевом лингере по идее должен идти RESET, а не закрытие сокета через FIN. Попробуйте устанавливать лингер при создании сокета. Возможно вы пропустили какие-то ситуации закрытия сокета, например когда закрытие соединения инициируется другой стороной.
+ если у вас протокол с долго живущими соединениями, которые могут подолгу простаивать без передачи данных, не забывайте использовать SO_KEEPALIVE, иначе клиенты за NAT’ом могут отваливаться втихую, что и приводит к многочисленным fin-wait’ам.

Как закрыть сокет linux

Пожалуйста,
1. Соблюдайте правила Форума.
2. Слушайте советы Модераторов.
(например, http://forum.sources.ru/index.php?act=ST&f=7&t=80382 )
3. Сверяйтесь с учебником по Великому и Могучему

‘> как правильно закрывать сокеты , не могу начать прослушивать порт

  • Подписаться на тему
  • Сообщить другу
  • Скачать/распечатать тему

Сообщ. #1 , 23.04.05, 23:24
Рейтинг (т): 0

Программа написана на C++ под Linux использует sys/socket.h и т.д.
Мой сервер не может начать прослушивать порт после первого сеанса с клиентом.
Проблема возникает при вызове функции bind. Интересно что спустя минуту сервер может
начать снова прослушивать порт. Сокеты правильно закрываю, то есть сначала сокет
через который с клиентом обшаюсь потом сокет который прослушивает порт, клиент тоже
закрывает соединение без ошибок. Неужели порт может быть доступен лиш спустя пару минут
в Линуксе (Knoppix) ? Могу предоставить исходник если нужно. Да кстати клиент и
сервер находятся в одном исполнительном файле

Сообщ. #2 , 24.04.05, 00:21

Рейтинг (т): 49
Хм. Приаттачивай исходник — будем смотреть.
Судя по твоему описанию — такой ошибки быть не должно
Сообщ. #3 , 24.04.05, 02:15

Это известная фича.

вкратце:
You can use setsockopt() to set the SO_REUSEADDR socket option

Сообщ. #4 , 24.04.05, 06:00
Рейтинг (т): 215

mipo
Как отметил grustnoe, это проблема с TimeWait
У вас и клиент, и сервер находятся на одной машине, значит обслуживаются одним и тем же стеком TCP/IP
После закрытия сокета информация о соединении в течении обычно 2-х минут сохраняется в таблице соединений модуля TCP, и он не даст вам запустить сервер снова в течении этого времени — оно нужно для убиения (отбрасывания) заблудившихся пакетов, которые могут прийти из сети и скомпроментировать новое соединение с таким же адресом сокета.
Решение вам подсказано. На форуме оно обсуждалось неоднократно, в том числе и здесь

Сообщ. #5 , 24.04.05, 10:25

Рейтинг (т): 49
Oleg2004, после корректного закрытия сокетов — такого быть не должно: не раз проверял на практике.
Сообщ. #6 , 24.04.05, 11:25
Рейтинг (т): 215

deil
Увы, мы об этом уже много беседовали с failerom.
По всем законам такое должно быть — см. У.Стивенс Unix, Разработка сетевых приложений.
К сожалению, можно констатировать, что большинство современных реализаций отошли от законов и действительно можно встретить системы, которые разрешают перезапуск сервера или клиента — да, могу согласиться, .failer даже ставил эксперименты на разных платформах.
Но например Linux RedHat такого не позволяет.

Сообщение отредактировано: Oleg2004 — 24.04.05, 11:31
Сообщ. #7 , 24.04.05, 11:32

Рейтинг (т): 49

Oleg2004, хм. Никогда с этим не сталкивался, вообще никогда.
надо обратиться к RFC по TCP/IP — у меня такое ощущение, что сам протокол устроен так, что не допускает

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

Сообщ. #8 , 24.04.05, 12:27
Рейтинг (т): 215
deil
Вот только один из вариантов работы состояния TIME-WAIT из RFC 793:

Состояние TIME-WAIT
Единственная вещь, которая может произойти в этом
состоянии — это повторная передача чужого сигнала FIN.
Подтвердить сигнал и повторно стартовать по истечении
контрольного времени 2MSL.

— это и есть обычно 2*2=4 мин.
TIME-WAIT — это официальное состояние конечного автомата TCP, и встречается оно в RFC-сплошь и рядом.
А после приема повторного FIN может прийти еще какой-то пакет заблудший, в общем ждать надо.

Сообщение отредактировано: Oleg2004 — 24.04.05, 12:30
Сообщ. #9 , 24.04.05, 13:34

Рейтинг (т): 49

Непонятки какие-то.
Я с обеих сторон закрыл соединение — зачем нужно это состояние, если учесть тот факт, что tcp/ip гарантирует мне доставку каждого пакета?

Добавлено 24.04.05, 13:38
Запустил сервер, подключился. Посмотрел вывод netstat — соединение есть.
Убил коннект, посмотрел вывод: соединения нет

Добавлено 24.04.05, 13:47
Почитал RFC:

TIME-WAIT — represents waiting for enough time to pass to be sure
the remote TCP received the acknowledgment of its connection
termination request.

Case 2: TCP receives a FIN from the network
If an unsolicited FIN arrives from the network, the receiving TCP
can ACK it and tell the user that the connection is closing. The
user will respond with a CLOSE, upon which the TCP can send a FIN to
the other TCP after sending any remaining data. The TCP then waits
until its own FIN is acknowledged whereupon it deletes the
connection. If an ACK is not forthcoming, after the user timeout
the connection is aborted and the user is told.

Здесь идёт не ожидание заблудшего пакета, а подтверждение приёма FIN’а удалённой стороной. После чего — соединение переходит в состояние CLOSED;
И раз сокет вешается на таком состоянии — значит проблемы с сетью: подтверждающий пакет не доходит до адресата..
В случае же, если всё делается как надо, никаких ожиданий нет!

Сообщ. #10 , 24.04.05, 15:11
я как-то пробовал прыгать с shutdown; close — нифига хорошего не вышло
Сообщ. #11 , 24.04.05, 15:44
Рейтинг (т): 215
deil

Запустил сервер, подключился. Посмотрел вывод netstat — соединение есть.
Убил коннект, посмотрел вывод: соединения нет

netstat.exe — прикладная программа, не имеющая доступа к таблицам модуля TCP

1.Сеть – это очень «непростой файл», и тот факт, что была вызвана функция ядра close() на сокете sd, еще не означает, что сразу же будут освобождены все ресурсы – буфера, структуры, записи в таблицах — с ним связанные. Это всего лишь означает, что этот сокет больше не будет доступен программе, но ядро будет удерживать его в состоянии TIME_WAIT. Время между вызовом close() и реальным освобождением сокета расчитывается исходя из максимального времени существования пакета в сети.
2.SO_REUSEADDR
Эта опция позволяет функции bind() разрешить многократное использование локальных адресов для этого сокета. Если пользоваться этой опцией, можно фактически иметь несколько сокетов с тем же самым номером Интернет-порта, однако IP-адреса должны быть разные. Такая ситуация возникает при использовании техники альтернативных IP-адресов (aliases – псевдонимов), когда один и тот же физический интерфейс имеет один основной (primary) IP-адрес и несколько альтернативных. В результате могут функционировать несколько экземпляров одного серверного процесса на одном и том же порту. Существует также и вариант полностью дублированного связывания, но он применим только для UDP при определенных условиях [19].
Необходимость в этой опции возникает потому, что некоторые протоколы Интернета более высокого уровня, такие FTP, требуют многократного использования того же самого сокета. Иногда это бывает необходимо также при реализации приложения для групповой рассылки.
Значение опции имеет тип int; значение отличное от нуля означает «да». Еще один вариант использования этой опции – это применение ее на серверной стороне с целью наиболее быстрого восстановления работоспособности «упавшего» сервера. Если сервер выполнил активное закрытие, он находится в состоянии TIME_WAIT, и модуль TCP будет помнить адрес сокета, к которому был «привязан» серверный порт, в течении 2MSL, что заставит его при немедленном перезапуске выдать сообщение «Address already in use».
3.Таймер задержки
Получателю могут поступать сегменты и после того, как соединение было им закрыто. Таймер задержки (quiet timer) связан с состоянием ожидания TIME_WAIT (см. раздел 1.7.13), которое предоставляет защиту от опоздавших пакетов, принадлежащих ранним соединениям; при этом они не будут интерпретироваться как часть нового соединения, которое использует те же самые локальный и удаленный IP адреса и номера портов. Это состояние иногда называется состоянием 2MSL – удвоенное время жизни TCP-сегмента Maximum Segment Lifetime.
Следуя RFC-793, в котором MSL определен равным 2 мин., общее время TIME_WAIT составит 4 мин. На практике это обычно не так. Например, в системах, производных от BSD, MSL равно 30 сек., так что состояние TIME_WAIT длится всего 1 мин. Можно встретить и другие значения в диапазоне от 30 сек. до 2 мин. Если в то время, когда процесс находится в состоянии TIME_WAIT, прибывает новый сегмент, то его таймер 2MSL перезапускается.
Возможно, что хост или его процесс, находясь в состоянии 2MSL, вышли из строя, за время 2MSL успели перезапуститься и немедленно установили новые соединения с использованием локальных и удаленных адресов сокетов, которые были в распоряжении процесса в состоянии 2MSL перед выходом из строя.
В этом случае опоздавшие сегменты из соединения, которое существовало перед выходом из строя сетевой программы хоста, ОС или самого хоста, могут быть ошибочно интерпретированы как принадлежащие новому соединению, созданному после перезапуска. Это может произойти вне зависимости от того, какой начальный номер последовательности сгенерировал модуль TCP после перезагрузки.
Чтобы защититься от подобных нежелательных ситуаций, RFC-793 указывает, что модуль TCP не должен создавать новые соединения до истечения MSL с момента перезагрузки. Этот период называется «тихое время» (quiet time).
В некоторых реализациях хосты после перезагрузки ожидают даже дольше, чем время MSL.
Длительность задержки обычно выбирают равной удвоенному значению максимального времени жизни сегмента (оно совпадает со значением поля времени жизни в заголовке IP-пакета). В ответ на каждый пришедший в этот период сегмент отправляется сообщение об ошибке.
.failer — это его цитата из переписки со мной по поводу SO_REUSEADDRESS

чтобы сервер мог быстро восстановиться после падения. То есть: когда сервер TCP или UDP аварийно завершает работу, сокет все еще болтается в памяти и еслиб не было механизма повторного использования адреса (Reuse Addresing) то сервак смог бы запуститься только через 2 с половиной минуты (это по RFC время нахождения сокета в памяти в случае отказа приложения).
Все серверы в настоящее время используют этот механизм.

Tак что если сервер сделан без опции SO_REUSEADDRESS, он не даст себя перезапустить в течение нескольких минут Это закон!

Сообщ. #12 , 24.04.05, 19:42

Рейтинг (т): 49
netstat.exe

мы в разделе UNIX
На моей системе оно всегда выводило все состояния, включая TIME_WAIT и проч;

В этом случае опоздавшие сегменты из соединения, которое существовало перед выходом из строя сетевой программы хоста, ОС или самого хоста, могут быть ошибочно интерпретированы как принадлежащие новому соединению, созданному после перезапуска. Это может произойти вне зависимости от того, какой начальный номер последовательности сгенерировал модуль TCP после перезагрузки.

Хочешь сказать, получатель не отличит «новые» пакеты от «старых»?

Так что если сервер сделан без опции SO_REUSEADDRESS, он не даст себя перезапустить в течение нескольких минут Это закон!

Как правильно закрыть сокет для последующего его открытия

У меня 2 формы(клиент сервер). На форме улиента есть кнопка Disconnect. По ее нажатию серверу отправляется команда «END» и он выполняет метод socket.Close. Однако когда после этого закрыть форму сервера и снова запустить, то вылетает ошибка, пишет, что порт используется. Жму End. Запускаю опять — ок.

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

Лучшие ответы ( 2 )
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
Ответы с готовыми решениями:

Как выбрать файл с диска для последующего открытия?
Подскажите как открыть, точнее, как выбрать файл, с помощью "обзора " то есть с помощью файлового.

Как правильно упаковать модуль для последующего вывода в материалы
Не могу никак упаковать модуль что бы потом его вывести в материал. Скажите что не так в файлах.

Функции для записи в сокет и чтения из сокета: как правильно задать для windows?
делала проект под Linux и в нем успешно работали write и read. Но теперь мне нужно.

Как создать массив экземпляров класса для последующего обращения к его элементам по ссылке?
Всем здравствуйте. Вот код моего класса: class Param < private: int cnt; float akk;.

2785 / 717 / 106
Регистрация: 04.02.2011
Сообщений: 1,443

Что за класс, библиотека у экземпляра «socket»? Точно не попутали форум? Если нет, прикрепите документ с формами и др. модулями, запаковав его в .zip, если файл имеет расширение, отличное от разрешенных.

Вежливость-главное оружие
233 / 234 / 86
Регистрация: 19.02.2013
Сообщений: 1,446

Насчет клааса не скажу, т.е. это сделано в Excel. Я подключал коспонент WinSock и использовал.

Вежливость-главное оружие
233 / 234 / 86
Регистрация: 19.02.2013
Сообщений: 1,446

ЦитатаСообщение от mc-black Посмотреть сообщение

Точно не попутали форум?
Это вы к чему?
2785 / 717 / 106
Регистрация: 04.02.2011
Сообщений: 1,443

Лучший ответ

Сообщение было отмечено Памирыч как решение

Решение

ЦитатаСообщение от some_name Посмотреть сообщение

Это вы к чему?

К тому что, подумал, что вопрос относится к другому языку программирования, так как VBA, Office, Windows не содержат по умолчанию никакого WinSock (во всяком случае его нет в моей конфигурации). Что это такое, я знаю, причем знаю по старому VB6 (как раз там такой контрол был). Использованное слово «компонент» вообще относится к Delphi, но никак не к VBA. Так как проект не был прикреплен к теме, а также не было никакого контрола на рабочем компе, отвечать не было особого смысла. Сейчас можно посмотреть.

Добавлено через 1 час 15 минут
К сожалению, этот контрол я себе установить не могу — нет админских прав, смог только посмотреть код. Есть предположение, что надо при отправке команды «END» не только закрывать сокет со стороны сервера, но и со стороны клиента, то есть в обработчике кнопки Send:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
Private Sub btnSendData_Click() Dim data As String If socketClient.State <> sckConnected Then MsgBox "Connection is not set up" Exit Sub End If If obSql.Value = True Then data = tbSqlQuety.Text Else data = tbMessage.Text End If socketClient.SendData data If data = "END" Then socketClient.Close MsgBox "Сервер отключен" End If End Sub

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *