Я знаю, что есть довольно много вопросов по SE, и я думаю, что прочитал столько, сколько это важно, прежде чем я подойду к этой точке.
Под «серверной стороной TIME_WAIT
» я подразумеваю состояние пары сокетов на стороне сервера, которая инициировала свой метод close () на стороне сервера.
Я часто вижу эти заявления, которые звучат противоречиво для меня:
- Серверная сторона
TIME_WAIT
безвредна - Вы должны спроектировать свои сетевые приложения так, чтобы клиенты запускали close (), поэтому клиент должен нести
TIME_WAIT
Причина, по которой я нахожу это противоречивым, заключается в том, что TIME_WAIT
на клиенте может быть проблема - клиент может использовать неиспользуемые порты, поэтому по сути вышесказанное рекомендует перенести бремя TIME_WAIT
на клиентскую сторону, где это может быть проблемой, с сторона сервера, где это не проблема.
Клиентская сторона, TIME_WAIT
конечно, является проблемой только для ограниченного числа вариантов использования. Большинство клиент-серверных решений будет включать в себя один сервер и множество клиентов, клиенты обычно не имеют достаточно большого количества соединений, чтобы это могло стать проблемой, и даже если они это сделают, есть ряд рекомендаций для «вменяемых» ( в отличие от SO_LINGER
тайм-аута 0 или вмешательства с sysctls tcp_tw) борются со стороной клиента TIME_WAIT
, избегая слишком быстрого создания слишком большого количества соединений. Но это не всегда возможно, например, для таких приложений, как:
- системы мониторинга
- генераторы нагрузки
- прокси
С другой стороны, я даже не понимаю, насколько серверная сторона TIME_WAIT
полезна вообще. Причина TIME_WAIT
даже в том, что она предотвращает внедрение устаревших TCP
фрагментов в потоки, которым они больше не принадлежат. Для клиентской стороны TIME_WAIT
это достигается просто невозможностью создания соединения с теми же ip:port
парами, которые могли иметь это устаревшее соединение (используемые пары заблокированы TIME_WAIT
). Но на стороне сервера это не может быть предотвращено, поскольку локальный адрес будет иметь принимающий порт, и всегда будет одинаковым, а сервер не может (AFAIK, у меня есть только эмпирическое доказательство) отрицать соединение просто потому, что входящий узел создаст ту же пару адресов, которая уже существует в таблице сокетов.
Я написал программу, которая показывает, что время ожидания на стороне сервера игнорируется. Более того, поскольку тест был выполнен на 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
бесполезен?
TIME_WAIT
.