По поводу моего предложения прочтите последний раздел: «Когда использовать SO_LINGER с таймаутом 0».
Прежде чем мы перейдем к этой небольшой лекции о:
- Нормальное завершение TCP
TIME_WAIT
FIN
, ACK
и RST
Нормальное завершение TCP
Обычная последовательность завершения TCP выглядит так (упрощенно):
У нас есть два партнера: A и B
- A calls
close()
- A sends
FIN
to B
- А переходит в состояние
FIN_WAIT_1
- B receives
FIN
- B sends
ACK
to A
- B переходит в
CLOSE_WAIT
состояние
- A receives
ACK
- A goes into
FIN_WAIT_2
state
- B calls
close()
- B sends
FIN
to A
- B переходит в
LAST_ACK
состояние
- A receives
FIN
- A sends
ACK
to B
- А переходит в состояние
TIME_WAIT
- B receives
ACK
- B goes to
CLOSED
state – i.e. is removed from the socket tables
ВРЕМЯ ЖДЕТ
Таким образом, одноранговый узел, который инициирует завершение, то есть сначала вызывает close()
, окажется в состоянии 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 Network Programming", страница 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
, чтобы отбросить ожидающие данные ».
Я бы рекомендовал это длинная статья, которая, как мне кажется, дает очень хороший ответ на ваш вопрос.
person
mgd
schedule
26.10.2012