Думаю, я понимаю формальный смысл этого варианта. В некотором устаревшем коде, с которым я сейчас работаю, эта опция используется. Заказчик жалуется на RST как ответ на FIN со своей стороны при закрытии соединения с его стороны.
Я не уверен, что смогу удалить его безопасно, так как не понимаю, когда его следует использовать.
Не могли бы вы привести пример, когда потребуется такая опция?
Ответы:
Типичная причина установки
SO_LINGER
нулевого тайм-аута заключается в том, чтобы избежать большого количества подключений, находящихся вTIME_WAIT
состоянии, связывающих все доступные ресурсы на сервере.Когда TCP-соединение закрывается чисто, конец, инициировавший закрытие («активное закрытие»), заканчивается тем, что соединение остается в
TIME_WAIT
течение нескольких минут. Поэтому, если ваш протокол - это протокол, в котором сервер инициирует закрытие соединения и включает очень большое количество краткосрочных соединений, то он может быть подвержен этой проблеме.Однако это не очень хорошая идея -
TIME_WAIT
существует по какой-то причине (чтобы гарантировать, что случайные пакеты из старых соединений не мешают новым соединениям). Лучше изменить ваш протокол так, чтобы клиент инициировал закрытие соединения, если это возможно.источник
TIME_WAIT
он не причинит вреда клиенту. Помните, как говорится в третьем издании «Сетевое программирование UNIX» (Стивенс и др.), Стр. 203: «Состояние TIME_WAIT - ваш друг и помогает нам. Вместо того, чтобы пытаться избежать состояния, мы должны его понять (раздел 2.7) . "По поводу моего предложения прочтите последний раздел: «Когда использовать SO_LINGER с таймаутом 0» .
Прежде чем мы перейдем к небольшой лекции о:
TIME_WAIT
FIN
,ACK
иRST
Обычное завершение TCP
Обычная последовательность завершения TCP выглядит так (упрощенно):
У нас есть два партнера: A и B
close()
FIN
BFIN_WAIT_1
состояниеFIN
ACK
ACLOSE_WAIT
состояниеACK
FIN_WAIT_2
состояниеclose()
FIN
ALAST_ACK
состояниеFIN
ACK
BTIME_WAIT
состояниеACK
CLOSED
состояние - т.е. удаляется из таблиц сокетовВРЕМЯ ЖДЕТ
Таким образом, одноранговый узел, который инициирует завершение - т.е. звонит
close()
первым, - окажется вTIME_WAIT
состоянии.Чтобы понять, почему
TIME_WAIT
штат - наш друг, прочтите раздел 2.7 в третьем издании «Сетевое программирование UNIX» Стивенса и др. (Стр. 43).Однако это может быть проблемой при наличии большого количества сокетов
TIME_WAIT
на сервере, поскольку в конечном итоге это может помешать принятию новых подключений.Чтобы обойти эту проблему, я видел, как многие предлагали установить параметр сокета SO_LINGER с таймаутом 0 перед вызовом
close()
. Однако это плохое решение, поскольку оно приводит к завершению TCP-соединения с ошибкой.Вместо этого разработайте протокол своего приложения таким образом, чтобы завершение соединения всегда инициировалось со стороны клиента. Если клиент всегда знает, когда он прочитал все оставшиеся данные, он может инициировать последовательность завершения. Например, браузер знает из
Content-Length
HTTP-заголовка, когда он прочитал все данные и может инициировать закрытие. (Я знаю, что в HTTP 1.1 он будет некоторое время держать его открытым для возможного повторного использования, а затем закроет.)Если серверу необходимо закрыть соединение, разработайте протокол приложения так, чтобы сервер запрашивал у клиента вызов
close()
.Когда использовать SO_LINGER с таймаутом 0
Опять же, согласно третьему изданию "UNIX Network Programming", страница 202-203, установка
SO_LINGER
тайм-аута 0 перед вызовомclose()
приведет к тому, что нормальная последовательность завершения не будет инициирована.Вместо этого одноранговый
close()
узел, устанавливающий эту опцию и вызывающий , отправитRST
(сброс соединения), который указывает на состояние ошибки, и именно так это будет восприниматься на другом конце. Обычно вы будете видеть такие ошибки, как «Сброс соединения одноранговым узлом».Поэтому в нормальной ситуации устанавливать
SO_LINGER
тайм-аут 0 перед вызовомclose()
- с этого момента - вызываемым неудачным закрытием - в серверном приложении - это действительно плохая идея .Тем не менее, определенная ситуация в любом случае требует этого:
CLOSE_WAIT
или попадания вTIME_WAIT
состояние.TIME_WAIT
(при вызовеclose()
со стороны сервера), поскольку это может помешать серверу получить доступные порты для новых клиентских подключений. после перезапуска.CLOSE_WAIT
попытке доставить данные на застрявший терминал. порт, но правильно сбросил бы застрявший порт, если бы получилRST
отмену ожидающих данных ".Я бы порекомендовал эту длинную статью, которая, как мне кажется, дает очень хороший ответ на ваш вопрос.
источник
TIME_WAIT
является другом только тогда, когда он не начинает вызывать проблемы: stackoverflow.com/questions/1803566/…Когда задержка включена, но таймаут равен нулю, стек TCP не ожидает отправки ожидающих данных перед закрытием соединения. Данные могут быть потеряны из-за этого, но, установив задержку таким образом, вы принимаете это и просите немедленно сбросить соединение, а не закрывать изящно. Это вызывает отправку RST, а не обычного FIN.
Спасибо EJP за его комментарий, подробности смотрите здесь .
источник
Сможете ли вы безопасно удалить задержку в своем коде или нет, зависит от типа вашего приложения: это «клиент» (открытие TCP-соединений и его активное закрытие в первую очередь) или «сервер» (прослушивание TCP открытых и закрытие после того, как другая сторона инициировала закрытие)?
Если ваше приложение имеет вид «клиента» (сначала закрывается), и вы инициируете и закрываете огромное количество подключений к разным серверам (например, когда ваше приложение является приложением для мониторинга, контролирующим доступность огромного количества разных серверов), ваше приложение проблема в том, что все ваши клиентские подключения застряли в состоянии TIME_WAIT. Затем я бы порекомендовал сократить тайм-аут до меньшего значения, чем значение по умолчанию, чтобы по-прежнему корректно завершать работу, но раньше освобождать ресурсы клиентских подключений. Я бы не установил тайм-аут на 0, так как 0 не завершает корректно с FIN, но прерывается с RST.
Если ваше приложение похоже на «клиент» и ему необходимо получить огромное количество небольших файлов с одного и того же сервера, вам не следует инициировать новое TCP-соединение для каждого файла и в конечном итоге получить огромное количество клиентских подключений в TIME_WAIT, но держите соединение открытым и извлекайте все данные через одно и то же соединение. Задерживающийся вариант можно и нужно убрать.
Если ваше приложение является «сервером» (вторая секунда как реакция на закрытие однорангового узла), при close () ваше соединение корректно завершается и ресурсы освобождаются, поскольку вы не входите в состояние TIME_WAIT. Задерживаться не следует. Но если в вашем серверном приложении есть процесс наблюдения, обнаруживающий, что неактивные открытые соединения бездействуют в течение длительного времени (следует определить «долго»), вы можете отключить это неактивное соединение со своей стороны - рассматривая это как своего рода обработку ошибок - с помощью прерывистого завершения. Это делается путем установки таймаута задержки на 0. Затем close () отправит клиенту RST, сообщая ему, что вы злитесь :-)
источник
На серверах вы можете отправлять сообщения
RST
вместоFIN
отключения некорректно работающих клиентов. Это пропускает,FIN-WAIT
за которым следуютTIME-WAIT
состояния сокетов на сервере, что предотвращает истощение ресурсов сервера и, следовательно, защищает от такого типа атак типа «отказ в обслуживании».источник
Мне нравится замечание Максима о том, что атаки DOS могут истощить ресурсы сервера. Это также происходит без действительно злонамеренного противника.
Некоторым серверам приходится иметь дело с «непреднамеренной атакой DOS», которая происходит, когда клиентское приложение имеет ошибку с утечкой соединения, когда они продолжают создавать новое соединение для каждой новой команды, которую они отправляют на ваш сервер. А затем, возможно, в конечном итоге их соединения закроются, если они столкнутся с давлением сборщика мусора, или, возможно, соединения в конечном итоге истекут.
Другой сценарий - сценарий «все клиенты имеют один и тот же TCP-адрес». Тогда клиентские соединения различимы только по номерам портов (если они соединяются с одним сервером). И если клиенты начинают быстро циклически открывать / закрывать соединения по любой причине, они могут исчерпать (адрес клиента + порт, IP-адрес сервера + порт) пространство кортежа.
Поэтому я думаю, что серверам лучше всего посоветовать переключиться на стратегию Linger-Zero, когда они видят большое количество сокетов в состоянии TIME_WAIT - хотя это не исправляет поведение клиента, это может уменьшить влияние.
источник
Слушающий сокет на сервере может использовать задержку со временем 0, чтобы иметь доступ к немедленной привязке к сокету и сбросить всех клиентов, чьи соединения еще не завершены. TIME_WAIT - это то, что интересно только тогда, когда у вас есть сеть с несколькими путями и может закончиться неправильно упорядоченными пакетами или иным образом иметь дело с нечетным порядком / временем прибытия сетевых пакетов.
источник