Опция TCP SO_LINGER (ноль) - когда это требуется - PullRequest
81 голосов
/ 21 сентября 2010

Мне кажется, я понимаю формальное значение опции.В некотором устаревшем коде, который я сейчас обрабатываю, используется опция.Заказчик жалуется на RST как ответ на FIN со своей стороны при закрытии соединения со своей стороны.

Я не уверен, что могу удалить его безопасно, так как не понимаю, когда его следует использовать.

Не могли бы вы привести пример того, когда потребуется опция?

Ответы [ 4 ]

165 голосов
/ 26 октября 2012

Для моего предложения, пожалуйста, прочитайте последний раздел: «Когда использовать SO_LINGER с таймаутом 0» .

Прежде чем мы перейдем к небольшой лекции о:

  • Обычное TCP-завершение
  • TIME_WAIT
  • FIN, ACK и RST

Обычное TCP-завершение

Обычная последовательность завершения TCP выглядит следующим образом (упрощенно):

У нас есть два партнера: A и B

  1. A вызывает close()
    • A отправляет FIN до B
    • A переходит в FIN_WAIT_1 состояние
  2. B получает FIN
    • B отправляет ACK в A
    • B переходит в CLOSE_WAIT состояние
  3. A получает ACK
    • A переходит в FIN_WAIT_2 состояние
  4. B звонит close()
    • B отправляет FIN в A
    • B переходит в LAST_ACK состояние
  5. A получает FIN
    • A отправляет ACK в B
    • A переходит в TIME_WAIT состояние
  6. B получает ACK
    • B переходит в состояние CLOSED - т.е. удаляется из таблиц сокетов

TIME_WAIT

Таким образом, узел, который инициирует завершение - т.е. вызывает close() first - перейдет в состояние TIME_WAIT.

Чтобы понять, почему состояние TIME_WAIT является нашим другом, прочитайте раздел 2.7 в третьем издании «Сетевое программирование UNIX» Стивенса и др. (Стр.43).

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

Чтобы обойти эту проблемуЯ видел, как многие предлагали установить опцию сокета SO_LINGER с таймаутом 0 перед вызовом close().Однако это плохое решение, поскольку оно приводит к разрыву TCP-соединения с ошибкой.

Вместо этого разработайте протокол приложения, чтобы прекращение подключения всегда инициировалось со стороны клиента.Если клиент всегда знает, когда он прочитал все оставшиеся данные, он может инициировать последовательность завершения.Например, браузер знает из заголовка HTTP Content-Length, когда он прочитал все данные и может инициировать закрытие.(Я знаю, что в HTTP 1.1 он некоторое время будет держать его открытым для возможного повторного использования, а затем закроет его.)

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

Когда использовать SO_LINGER с таймаутом 0

Опять же, в соответствии с «Сетевым программированием UNIX», третья страница издания 202-203, установка SO_LINGER с таймаутом 0 довызов close() вызовет нормальную последовательность завершения , а не .

Вместо этого узел, устанавливающий эту опцию и вызывающий close(), отправит RST (сброс соединения)что указывает на состояние ошибки, и именно так оно будет восприниматься на другом конце.Обычно вы будете видеть ошибки типа «Сброс соединения по пиру».

Поэтому в обычной ситуации очень плохая идея установить SO_LINGER с таймаутом 0 до вызова close() - с этого момента вызывается аварийное закрытие - в серверном приложении.

Тем не менее, определенная ситуация оправдывает это в любом случае:

  • Если клиент вашего серверного приложения работает неправильно (время ожидания истекло), возвращает неверные данные и т. д.) при сбое закрытия имеет смысл избегать застревания в CLOSE_WAIT или попадания в состояние TIME_WAIT.
  • Если вам необходимо перезапустить серверное приложение, в котором на данный момент находятся тысячи клиентовСоединения, которые вы могли бы рассмотреть, установив этот параметр сокета, чтобы избежать тысяч серверных сокетов в TIME_WAIT (при вызове close() со стороны сервера), так как это может помешать серверу получать доступные порты для новых клиентских соединений после перезапуска.
  • На странице202 в вышеупомянутой книге, в частности, говорится: «Существуют определенные обстоятельства, которые требуют использования этой функции для отправки аварийного закрытия. Одним из примеров является терминальный сервер RS-232, который может зависнуть навсегда в CLOSE_WAIT, пытаясь доставить данные застрявшемупорт терминала, но он будет правильно сбрасывать застрявший порт, если он получит RST, чтобы отбросить ожидающие данные. "очень хороший ответ на ваш вопрос.
69 голосов
/ 21 сентября 2010

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

15 голосов
/ 21 сентября 2010

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

Спасибо EJP за его комментарий, см. здесь для деталей.

4 голосов
/ 11 января 2018

Можно ли безопасно удалить задержку в своем коде или нет, зависит от типа вашего приложения: это «клиент» (сначала открывает TCP-соединения и активно его закрывает) или это «сервер» (слушаетTCP открывает и закрывает его после того, как другая сторона инициирует закрытие)?

Если ваше приложение имеет вид «клиента» (сначала закрытие) И вы инициируете и закрываете огромное количество подключений к различным серверам (например,если ваше приложение представляет собой приложение для мониторинга, контролирующее доступность огромного количества различных серверов), в вашем приложении возникает проблема, заключающаяся в том, что все ваши клиентские подключения застряли в состоянии TIME_WAIT.Затем я бы порекомендовал сократить время ожидания до меньшего значения, чем значение по умолчанию, чтобы по-прежнему корректно завершать работу, но раньше освобождать ресурсы клиентских подключений.Я бы не стал устанавливать тайм-аут на 0, так как 0 не завершается корректно с FIN, но не срабатывает с RST.

Если ваше приложение имеет вид «клиента» и должно извлекать огромное количество небольших файлов изтот же сервер, вы не должны инициировать новое TCP-соединение для каждого файла и в конечном итоге получить огромное количество клиентских соединений в TIME_WAIT, но держать соединение открытым и извлекать все данные по одному и тому же соединению.Опция Linger может и должна быть удалена.

Если ваше приложение является «сервером» (секунда закрытия, как реакция на закрытие однорангового узла), при закрытии () ваше соединение корректно завершается, а ресурсы освобождаются, как вы этого не делаете.t войти в состояние TIME_WAIT.Задержка не должна использоваться.Но если в вашем северном приложении есть надзорный процесс, который обнаруживает неактивные открытые соединения, простаивающие в течение длительного времени («длинное» должно быть определено), вы можете отключить это неактивное соединение со своей стороны - рассматривайте это как вид обработки ошибок - с аварийным отключением.Это можно сделать, установив задержку linger на 0. close () затем отправит RST клиенту, сообщив ему, что вы злитесь: -)

...