Параметр TCP SO_LINGER (ноль) – когда требуется

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

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

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

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

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

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

По моему предложению, пожалуйста, прочитайте последний раздел: «Когда использовать 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() первым, попадет в TIME_WAIT .

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

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

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

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

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

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

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

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

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

Однако, определенная ситуация оправдывает это так:

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

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

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

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

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

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

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

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

  • Как обнаружить ближайший боковой разъем?
  • Установка таймаута для операций сокета
  • Получить MAC-адрес на локальной машине с помощью Java
  • Единство: streamовое видео в реальном времени
  • Содержит ли соединение сокетов TCP «поддерживать»?
  • Что вы используете, когда вам нужен надежный UDP?
  • C # Установленное соединение было прервано программным обеспечением вашей хост-машины
  • Как выйти из вызова recv () блокировки?
  • Почему невозможно, не пытаясь выполнить ввод-вывод, обнаружить, что TCP-сокет был изящно закрыт одноранговым узлом?
  • Использование .Net 4.5 Функция Async для программирования сокетов
  • Почему писать закрытый TCP-разъем хуже, чем читать?
  • Interesting Posts

    В чем разница между методом и селектором?

    Как подсчитать ячейки в диапазоне со значением меньше, чем другая ячейка в excel?

    Быстрее ли подсчитывать, чем подсчитывать?

    Сделать JScrollPane управление несколькими компонентами

    Как добавить загрузочные приложения в Lubuntu / Lxde?

    Android Firebase DynamiteModule: не удалось загрузить дескриптор модуля

    Как принудительно создать экземпляр экземпляра C ++ для экземпляра?

    Виртуальный / чистый виртуальный пояс C ++

    Возможно ли иметь возможность правильно выбрать любую доступную учетную запись Google для использования при авторизации через клиентскую библиотеку JS для Диска?

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

    jQuery: Разница между позицией () и смещением ()

    Включить SQL Server ‘xp_cmdshell’

    выборочная проверка mongoose с использованием 2 полей

    Несколько входных данных в JOptionPane.showInputDialog

    Клонирование только пространства, используемого с жесткого диска

    Давайте будем гением компьютера.