Трёхстороннее рукопожатие TCP
Трёхстороннее рукопожатие TCP
Любое TCP-соединение — будь то загрузка сайта, отправка письма или видеозвонок — начинается с одного и того же ритуала: трёх шагов, занимающих доли секунды. Этот ритуал называется трёхстороннее рукопожатие (three-way handshake). Разберём, что происходит в эти три шага и почему их именно три.
Зачем вообще нужно рукопожатие
Прежде чем передавать данные, TCP должен убедиться, что обе стороны живы и готовы к общению. Также на этапе рукопожатия стороны согласуют начальные порядковые номера (sequence numbers) — они выбираются случайно, чтобы старые пакеты от предыдущих соединений не были ошибочно приняты за новые.
Шаг за шагом
Клиент хочет установить соединение с сервером. Начинается танец:
Шаг 1: SYN (Synchronize). Клиент → Сервер.
Клиент отправляет TCP-сегмент с флагом SYN и случайным начальным порядковым номером SEQ = x (например, SEQ = 1000). Никаких данных в этом сегменте ещё нет — это чистый запрос на соединение.
Клиент: "Привет, я хочу общаться. Мой начальный номер: 1000."
Шаг 2: SYN-ACK (Synchronize-Acknowledgment). Сервер → Клиент.
Сервер отвечает сегментом с двумя флагами: SYN (я тоже хочу синхронизироваться) и ACK (я подтверждаю твой номер). Сервер устанавливает свой начальный порядковый номер SEQ = y (например, SEQ = 5000) и подтверждает номер клиента: ACK = x + 1 (то есть ACK = 1001).
Сервер: "Принято. Мой начальный номер: 5000. Жду от тебя байт 1001."
Шаг 3: ACK (Acknowledgment). Клиент → Сервер.
Клиент подтверждает получение SYN-сегмента сервера: ACK = y + 1 (то есть ACK = 5001). В этом сегменте уже могут быть данные.
Клиент: "Принял. Начинаем передачу данных."
Соединение установлено. Все три шага вместе занимают ровно полтора круговых времени (RTT): SYN туда, SYN-ACK обратно, ACK туда.
Клиент Сервер
│─────────── SYN ────────────────→│
│ │
│←──────── SYN-ACK ──────────────│
│ │
│─────────── ACK ────────────────→│
│ │
│═══ соединение установлено ═══════│
│ (можно слать данные) │
Почему именно три шага, а не два
Если бы хватало двух шагов, возникала бы проблема старых дублированных SYN-запросов. Представь: клиент отправил SYN, он где-то задержался, клиент подумал, что сервер не отвечает, и отправил новый SYN. Первый запрос наконец дошёл, сервер ответил SYN-ACK и считает соединение установленным — а клиент уже установил другое соединение и знать не знает о первом. Сервер висит с полуоткрытым соединением, зря тратя ресурсы.
Третий шаг (ACK от клиента) гарантирует, что клиент действительно хотел установить это соединение, а не просто «запоздавшее эхо».
Завершение соединения: четырёхстороннее рукопожатие
Закрытие TCP-соединения, в отличие от открытия, требует четырёх шагов, потому что каждая сторона закрывает свою половину соединения независимо:
- Клиент → Сервер: FIN (я закончил отправлять данные).
- Сервер → Клиент: ACK (я подтверждаю, что ты закончил).
- Сервер → Клиент: FIN (я тоже закончил).
- Клиент → Сервер: ACK (подтверждаю, что ты закончил).
После шага 2 клиент больше не отправляет данные, но всё ещё может их принимать — это называется полузакрытое состояние. Сервер может ещё какое-то время допередать оставшиеся данные и только потом послать свой FIN.
Состояния TCP-соединения
TCP-соединение проходит через несколько состояний. Самые важные:
- LISTEN — сервер ждёт входящих соединений.
- SYN-SENT — клиент отправил SYN, ждёт SYN-ACK.
- SYN-RECEIVED — сервер получил SYN, отправил SYN-ACK, ждёт ACK.
- ESTABLISHED — соединение установлено, идёт передача данных.
- FIN-WAIT — одна сторона инициировала закрытие, ждёт FIN от другой.
- TIME-WAIT — соединение закрыто, но порт удерживается ещё ~2 минуты, чтобы гарантировать, что запоздавшие пакеты не будут приняты за новое соединение.
Состояние TIME-WAIT — частая причина ошибки «Address already in use» при перезапуске серверов: порт ещё не освободился после предыдущего соединения.
Проверь себя
- Сколько времени занимает установка TCP-соединения?
- Зачем нужны случайные начальные порядковые номера?
- Что такое полузакрытое состояние?
- Полтора RTT (round-trip time). SYN + SYN-ACK = 1 RTT, ACK = 0.5 RTT (клиент может начать слать данные вместе с ACK).
- Чтобы старые пакеты от предыдущего соединения с теми же адресами и портами не были ошибочно приняты за данные нового соединения случайные номера делают такую путаницу практически невозможной.
- Это состояние после того, как одна сторона отправила FIN и получила ACK, но другая сторона ещё не отправила свой FIN. Первая сторона уже не отправляет данные, но всё ещё принимает.
Что унести с урока
- TCP-соединение устанавливается за три шага: SYN → SYN-ACK → ACK.
- Три шага, а не два, чтобы защититься от старых дублированных запросов.
- Закрытие требует четырёх шагов: FIN → ACK → FIN → ACK.
- Случайные начальные номера предотвращают пересечение старых и новых пакетов.
- Состояние TIME-WAIT (~2 минуты) защищает от перемешивания пакетов разных соединений.
TCP — король надёжности. Но что если надёжность не нужна, а важна каждая миллисекунда? В следующем уроке познакомимся с UDP — протоколом, который жертвует гарантиями ради скорости.