Получить доступ

Как устанавливается и закрывается TCP-соединение?

MiddleСети
Ещё спрашивают так:
  • Что такое трёхстороннее рукопожатие?
  • Зачем нужно состояние TIME_WAIT?
  • Почему на сервере тысячи соединений в TIME_WAIT?
Коротко

Установка — SYN → SYN-ACK → ACK: стороны обмениваются начальными номерами последовательности. Один оборот (RTT) до первых данных.

  • Закрытие — FIN и ACK в каждую сторону, соединение закрывается по половинам
  • Кто закрыл первым, ждёт в TIME_WAIT 2×MSL (в Linux 60 с)
  • Много TIME_WAIT у клиента — новый коннект на каждый запрос; лечится пулом и keep-alive

А ещё SYN flood и SYN cookies; RST — аварийный разрыв

Так подсказку видит пользователь sotto на живом собеседовании — за пару секунд, пока вы думаете вслух.

Попробуйте сами

TCP: рукопожатие, потери и повторная передача

Подробно

Установка соединения:

  1. клиент шлёт SYN со своим начальным номером последовательности (ISN, случайный — защита от подделки);
  2. сервер отвечает SYN-ACK: свой ISN и подтверждение номера клиента + 1;
  3. клиент подтверждает ACK. Соединение установлено, можно слать данные.

Зачем три шага, а не два: обе стороны должны убедиться, что другая получила их номер последовательности. Цена — один RTT до начала обмена; с TLS 1.3 добавляется ещё один, отсюда интерес к keep-alive, пулам и QUIC.

На сервере полуоткрытые соединения (получен SYN) лежат в очереди SYN, а установленные, но ещё не принятые accept(), — в очереди accept (размер — backlog в listen и somaxconn). Переполнение очереди при всплеске — потерянные соединения. SYN flood — атака множеством SYN без завершения; защита — SYN cookies: сервер не хранит состояние, а кодирует его в своём ISN.

Закрытие. TCP двунаправлен, каждая сторона закрывает свою половину: FIN → ACK, затем FIN → ACK с другой стороны (часто FIN и ACK объединяются). Можно закрыть запись и продолжать читать (half-close).

TIME_WAIT. Сторона, закрывшая первой, после последнего ACK ждёт 2×MSL (максимальное время жизни сегмента; в Linux фиксированно 60 секунд). Зачем: если последний ACK потерялся, другая сторона повторит FIN, и на него надо ответить; и чтобы опоздавшие сегменты старого соединения не попали в новое с теми же адресами и портами.

Практика. Тысячи соединений в TIME_WAIT у клиента — признак того, что на каждый запрос открывается новое соединение. Каждое держит локальный порт, а их около 28 тысяч в диапазоне по умолчанию — при высоком RPS к одному адресу порты кончаются (Cannot assign requested address). Решение — переиспользовать соединения: HTTP keep-alive, пул соединений к базе. tcp_tw_reuse помогает исходящим соединениям; старый tcp_tw_recycle был удалён, потому что ломал клиентов за NAT.

RST — аварийный сброс: порт закрыт (connection refused), приложение закрыло сокет с непрочитанными данными, или соединение неизвестно.

Пример

$ ss -tan state time-wait | wc -l
27841
$ ss -tan | awk '{print $1}' | sort | uniq -c
  27841 TIME-WAIT
    112 ESTAB
# клиент открывает соединение на каждый HTTP-запрос — нужен пул/keep-alive

Что хотят услышать

  • Три шага и зачем обмен номерами последовательности.
  • Закрытие по половинам, FIN и ACK.
  • Смысл TIME_WAIT и кто в нём оказывается.
  • Практика: исчерпание портов, keep-alive и пулы; очередь accept и SYN flood — плюс.

Частые ошибки

  • Считать TIME_WAIT ошибкой, которую надо отключить.
  • Думать, что в TIME_WAIT попадает всегда сервер.

Что спросят дальше

Связанные вопросы

Закрытая бета

Подсказки на собеседовании — в реальном времени

sotto слышит вопрос интервьюера и показывает короткий ответ и план, как в блоке «Коротко». Оставьте контакт — пришлём доступ и промокод.

  • Промокод на 2 часа — сверх пробных 30 минут
  • Программа для Windows — скачать сразу, войти — через Яндекс ID или VK ID
  • Прямая связь с разработчиком: что улучшить — сделаем