Как устанавливается и закрывается TCP-соединение?
- Что такое трёхстороннее рукопожатие?
- Зачем нужно состояние 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 — аварийный разрыв
Подробно
Установка соединения:
- клиент шлёт SYN со своим начальным номером последовательности (ISN, случайный — защита от подделки);
- сервер отвечает SYN-ACK: свой ISN и подтверждение номера клиента + 1;
- клиент подтверждает 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 попадает всегда сервер.
Проверьте себя
Один верный вариант. Подумайте, потом откройте ответ.
Показать варианты и ответ
- ✓Установка SYN → SYN-ACK → ACK; закрывший первым ждёт в TIME_WAIT
- ✗TCP-соединение устанавливается одним пакетом
- ✗TIME_WAIT возникает у того, кто закрыл соединение последним
- ✗Много TIME_WAIT лечится отключением keep-alive
Что спросят дальше
Разбор темы
- TCP изнутри — рукопожатие, надёжность и перегрузка
Как устанавливается и закрывается соединение, откуда TIME_WAIT, как TCP гарантирует доставку и не перегружает сеть, что добавляет TLS.
Связанные вопросы
Разбор собеседования с sotto
После собеседования sotto разбирает разговор: оценка, каждый вопрос — хорошо, частично или слабо, чего не хватило в ответе и над чем поработать к следующему разу. Первые 30 минут бесплатно.