Назад | Перейти на главную страницу

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

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

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

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

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

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

Сторона клиента TIME_WAIT это, конечно, проблема только для ограниченного числа случаев использования. В большинстве клиент-серверных решений задействован один сервер и много клиентов, клиенты обычно не имеют дело с достаточно большим объемом соединений, чтобы это было проблемой, и даже если они это сделают, существует ряд рекомендаций для «разумного» ( в отличие от SO_LINGER с 0 таймаутом или вмешательство в 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, по умолчанию net config.

$ 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() праведно проиграл

Подводя итог, можно задать два вопроса:

В TCP Термины «сторона сервера» здесь означают хост, сокет которого находится в состоянии 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. Я бы ожидал, что на сокете также должен быть соответствующий пассивный OPEN (сокет в состоянии LISTEN).

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

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

это вероятно, самый ясный пример того, что на самом деле делает TIME-WAIT, и, что более важно, почему это важно. Это также объясняет, почему следует избегать некоторых «экспертных» советов по машинам Linux, чтобы «сократить» ВРЕМЯ-ОЖИДАНИЕ.

Сеанс tcp идентифицируется тупплом (sourceIP, sourcePort, destIP, destPort). Следовательно, TIME_WAIT работает на каждом TCP-соединении.

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

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