TCP option SO LINGER ( zero) - когда это необходимо



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



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



не могли бы вы привести пример, когда этот вариант будет необходим?

1047   4  

4 ответов:

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

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

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

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

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

  • нормальное завершение TCP
  • TIME_WAIT
  • FIN,ACK и RST

нормальное завершение TCP

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

у нас есть два сверстника: а и B

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

TIME_WAIT, прежде чем

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

чтобы понять, почему TIME_WAIT state-наш друг, пожалуйста, прочитайте раздел 2.7 в третьем издании "Unix Network Programming" Стивенса и др. (стр. 43).

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

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

вместо этого создайте свой протокол приложения, чтобы завершение соединения всегда инициировалось со стороны клиента. Если клиент всегда знает, когда он прочитал все оставшиеся данные, он может инициировать последовательность завершения. В качестве примера, браузер знает, что от Content-Length HTTP-заголовок, когда он прочитал все данные и может инициировать закрытие. (Я знаю, что в 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 для удаления ожидающих данных."

я бы порекомендовал этой большая статья, которая, я считаю, дает очень хороший ответ на ваш вопрос.

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

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

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

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

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

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

Comments

    Ничего не найдено.