Как работает TCP соединение и его установка
Что проверяют
Понимаете ли вы, что происходит под requests.get() или asyncio.open_connection(), и можете ли связать теорию с проблемами в проде: таймаутами, исчерпанием портов, склеенными сообщениями. Следом часто спрашивают, как закрывается соединение и зачем нужен TIME_WAIT.
Как отвечать
TCP даёт приложению надёжный упорядоченный поток байтов поверх IP, который сам ничего не гарантирует. Держится это на номерах последовательности, подтверждениях и повторной отправке.
Установка соединения, трёхстороннее рукопожатие:
- SYN: клиент отправляет свой случайный начальный номер последовательности (ISN) и переходит в
SYN_SENT; - SYN-ACK: сервер подтверждает номер клиента, увеличенный на единицу, и сообщает свой ISN;
- ACK: клиент подтверждает номер сервера, обе стороны переходят в
ESTABLISHED.
В Python всё это делает ядро. sock.connect() блокируется ровно на время рукопожатия. На сервере listen(backlog) задаёт размер очереди, а accept() отдаёт уже установленное соединение. Пустой результат recv() означает, что собеседник закрыл свою сторону.
Закрытие обычно занимает четыре сегмента: каждая сторона шлёт свой FIN и получает на него ACK, потому что поток можно закрыть в одну сторону и продолжать читать. Инициатор закрытия уходит в TIME_WAIT на 2 MSL, в Linux это 60 секунд, чтобы запоздавшие пакеты не попали в новое соединение с теми же портами.
Дальше надёжность: подтверждения, повторная отправка по таймауту, окно получателя для управления потоком и управление перегрузкой (в Linux по умолчанию CUBIC).
Где спотыкаются
Считают, что recv(1024) вернёт ровно одно отправленное сообщение. TCP это поток, границ сообщений в нём нет: придёт и половина, и два сразу. Нужно своё кадрирование, длина в заголовке или разделитель.
Используют send() вместо sendall(). send может отправить только часть буфера и вернуть число отправленных байт, и если результат не проверить, остаток просто не уйдёт.
Открывают новое соединение на каждый запрос. Каждый раз это лишний круг рукопожатия плюс TLS и тысячи сокетов в TIME_WAIT. Спасает requests.Session или клиент httpx с пулом соединений.