Как на самом деле работает `TIME_WAIT` на стороне сервера?

Как на самом деле работает `TIME_WAIT` на стороне сервера?

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

Под «серверной стороной TIME_WAIT» я подразумеваю состояние пары сокетов на стороне сервера, у которой close() был инициирован на стороне сервера.

Я часто вижу эти утверждения, которые кажутся мне противоречивыми:

  1. Серверная часть TIME_WAITбезвредна
  2. Вам следует проектировать сетевые приложения таким образом, чтобы клиенты инициировали close(), поэтому клиент должен нести ответственностьTIME_WAIT

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

Клиентская сторона, TIME_WAITконечно, является проблемой только для ограниченного числа случаев использования. Большинство клиент-серверных решений будут включать один сервер и много клиентов, клиенты обычно не имеют дело с достаточно большим объемом соединений, чтобы это было проблемой, и даже если они имеют дело, есть ряд рекомендаций, чтобы "разумно" (в отличие от SO_LINGERнулевого тайм-аута или вмешательства в tcp_tw sysctls) бороться с клиентской стороной, TIME_WAITизбегая создания слишком большого количества соединений слишком быстро. Но это не всегда осуществимо, например, для класса приложений, таких как:

  • системы мониторинга
  • генераторы нагрузки
  • прокси

С другой стороны, я даже не понимаю, как TIME_WAITвообще может помочь серверная часть. Причина TIME_WAITдаже там, потому что она предотвращает внедрение устаревших TCPфрагментов в потоки, которым они больше не принадлежат. Для клиентской части TIME_WAITэто достигается просто тем, что делает невозможным создание соединения с теми же ip:portпарами, которые могло бы иметь это устаревшее соединение (используемые пары заблокированы TIME_WAIT). Но для серверной части это невозможно предотвратить, поскольку локальный адрес будет иметь принимающий порт и всегда будет одним и тем же, и сервер не может (AFAIK, у меня есть только эмпирическое доказательство) отклонить соединение просто потому, что входящий одноранговый узел создаст ту же пару адресов, которая уже существует в таблице сокетов.

Я написал программу, которая показывает, что TIME-WAIT на стороне сервера игнорируются. Более того, поскольку тест проводился на 127.0.0.1, ядро ​​должно иметь специальный бит, который даже сообщает ему, является ли это стороной сервера или стороной клиента (иначе кортеж был бы тем же самым).

Источник:http://pastebin.com/5PWjkjEf, протестировано на Fedora 22, конфигурация сети по умолчанию.

$ gcc -o rtest rtest.c -lpthread
$ ./rtest 44400 s # will do server-side close
Will initiate server close
... iterates ~20 times successfully
^C
$ ss -a|grep 44400
tcp    TIME-WAIT  0      0            127.0.0.1:44400         127.0.0.1:44401   
$ ./rtest 44500 c # will do client-side close
Will initiate client close
... runs once and then
connecting...
connect: Cannot assign requested address

Таким образом, для серверной стороны TIME_WAITсоединения на той же самой паре портов могли быть восстановлены немедленно и успешно, а для клиентской стороны TIME-WAITна второй итерации connect()справедливо терпели неудачу.

Подводя итог, можно сказать, что вопрос состоит из двух частей:

  • Неужели серверная часть TIME_WAITдействительно ничего не делает и остаётся такой только потому, что RFCэтого требует?
  • Является ли рекомендация клиенту инициировать close() причиной того, что сервер TIME_WAITбесполезен?

решение1

ВТКППод серверной стороной здесь подразумевается хост, сокет которого находится в состоянии LISTEN.

RFC1122позволяет сокету в состоянии TIME-WAIT принимать новое соединение с некоторыми условиями

        When a connection is closed actively, it MUST linger in
        TIME-WAIT state for a time 2xMSL (Maximum Segment Lifetime).
        However, it MAY accept a new SYN from the remote TCP to
        reopen the connection directly from TIME-WAIT state, if it:

Подробную информацию об условиях см.RFC1122. Я ожидаю, что на сокете также должно быть соответствующее пассивное ОТКРЫТИЕ (сокет в состоянии LISTEN).

Активный OPEN (вызов соединения на стороне клиента) не имеет такого исключения и должен выдавать ошибку, когда сокет находится в состоянии TIME-WAIT, согласноRFC793.

Мое предположение относительно рекомендации по клиенту (в терминах TCP хост, выполняющий активное ОТКРЫТИЕ, т.е. подключение) инициировать закрытие во многом совпадает с вашим, что в общем случае он распространяет сокеты TIME-WAIT на большее количество хостов, где есть избыток ресурсов для сокетов. В общем случае клиенты не отправляют SYN, который повторно использовал бы сокеты TIME-WAIT на сервере. Я согласен, что применение такой рекомендации все еще зависит от варианта использования.

решение2

Клиент будет использовать новый TCP ISN (начальный порядковый номер) на основе алгоритма (увеличивается на 1 примерно каждые 4 микросекунды), и ISN в основном всегда больше порядкового номера, отправленного в последнем том же FIN TCP-сокета «пара ip:port», поэтому сервер всегда будет принимать новый SYN, даже если «пара ip:port» все еще записана в состоянии TIME_WAIT на сервере.

   RFC 793 [RFC0793] suggests that the choice of the ISN of a connection
   is not arbitrary, but aims to reduce the chances of a stale segment
   from being accepted by a new incarnation of a previous connection.
   RFC 793 [RFC0793] suggests the use of a global 32-bit ISN generator
   that is incremented by 1 roughly every 4 microseconds.

решение3

Этотвероятно, самый наглядный пример того, что на самом деле делает TIME-WAIT и, что еще важнее, почему это важно. Это также объясняет, почему следует избегать некоторых "экспертных" советов на машинах Linux, чтобы "уменьшить" TIME-WAIT.

решение4

С ненадежным протоколом вы никогда не можете быть уверены, что получили последнее сообщение от вашего однорангового устройства, поэтому опасно предполагать, что ваш одноранговый узел внезапно повесил трубку. Серьезным недостатком протокола TCP является то, что одновременно может быть открыто только около 65000 портов. Но преодолеть это можно, перейдя на ферму серверов, которая лучше масштабируется с нагрузкой, чем быстро перерабатывая номера портов. На стороне клиента крайне маловероятно, что у вас закончатся порты, если это базовая рабочая станция.

Связанный контент