TCP option SO LINGER ( zero) - когда это необходимо
Я думаю, что понимаю формальный смысл этого варианта. В некоторых устаревших кодах, которые я обрабатываю сейчас, используется опция. Клиент жалуется на RST как ответ на FIN со своей стороны при подключении близко со своей стороны.
Я не уверен, что смогу удалить его безопасно, так как я не понимаю, когда он должен быть использован.
не могли бы вы привести пример, когда этот вариант будет необходим?
4 ответов:
типичная причина для установки
SO_LINGERтайм-аут нуля, чтобы избежать большого количества соединений, сидящих вTIME_WAITсостояние, связывающее все доступные ресурсы на сервере.когда соединение TCP закрыто чисто, конец, который инициировал закрытие ("активное закрытие"), заканчивается соединением, сидящим в
TIME_WAITв течение нескольких минут. Так что если ваш протокол, где сервер инициирует закрытие соединения и включает в себя очень большое количество недолговечные соединения, то он может быть восприимчив к этой проблеме.это не очень хорошая идея, хотя -
TIME_WAITсуществует по какой-то причине (чтобы гарантировать, что случайные пакеты из старых соединений не мешают новым соединениям). Лучше перепроектировать свой протокол в тот, где клиент инициирует закрытие соединения, если это возможно.
для моего предложения, пожалуйста, прочитайте последний раздел: "когда использовать SO_LINGER с таймаутом 0".
прежде чем мы перейдем к этой небольшой лекции о:
- нормальное завершение TCP
TIME_WAITFIN,ACKиRSTнормальное завершение TCP
обычная последовательность завершения TCP выглядит так (упрощенно):
у нас есть два сверстника: а и B
- вызовы
close()
- посылает
FINб- переходит в
FIN_WAIT_1state- B получает
FIN
- B отправляет
ACKдо- B входит в
CLOSE_WAITstate- получает
ACK
- переходит в
FIN_WAIT_2state- B вызывает
close()
- B отправляет
FINдо- B идет в
LAST_ACKstate- получает
FIN
- посылает
ACKб- переходит в
TIME_WAITstate- B получает
ACK
- B идет в
CLOSEDсостояние-т. е. удаляется из таблиц сокетовTIME_WAIT, прежде чем
Итак, одноранговый узел, который инициирует завершение-т. е. вызывает
close()первый – будет в конечном итоге вTIME_WAITгосударство.чтобы понять, почему
TIME_WAITstate-наш друг, пожалуйста, прочитайте раздел 2.7 в третьем издании "Unix Network Programming" Стивенса и др. (стр. 43).однако, это может быть проблемой с большим количеством сокетов в
TIME_WAITсостояние на сервере, поскольку это может в конечном итоге предотвратить принятие новых подключений.чтобы обойти эту проблему, я видел много предложений установить параметр сокета SO_LINGER с таймаутом 0 перед вызовом
close(). Однако это плохое решение, поскольку оно приводит к тому, что TCP-соединение завершается с ошибкой.вместо этого создайте свой протокол приложения, чтобы завершение соединения всегда инициировалось со стороны клиента. Если клиент всегда знает, когда он прочитал все оставшиеся данные, он может инициировать последовательность завершения. В качестве примера, браузер знает, что от
Content-LengthHTTP-заголовок, когда он прочитал все данные и может инициировать закрытие. (Я знаю, что в 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