TCP: надёжное соединение

Разбираем TCP — протокол, который гарантирует надёжную и упорядоченную доставку данных.

TCP (Transmission Control Protocol) — транспортный протокол с установлением соединения, который гарантирует, что данные дойдут полностью, без потерь и в правильном порядке.

Что обещает TCP

IP-пакеты могут теряться, дублироваться, приходить не по порядку — сеть это не гарантирует. TCP надстраивается сверху и даёт приложению надёжный «поток байтов»: что отправили — то и получили, в том же порядке. Цена — накладные расходы и задержки. На TCP работают HTTP, почта, SSH — всё, где потеря данных недопустима.

Трёхстороннее рукопожатие (3-way handshake)

Прежде чем слать данные, стороны устанавливают соединение в три шага, обмениваясь начальными номерами последовательности (seq):

# Моделируем номера в 3-way handshake (без реальной сети)
client_seq = 1000
server_seq = 5000

print(f'1. Клиент -> Сервер: SYN, seq={client_seq}')
print(f'2. Сервер -> Клиент: SYN+ACK, seq={server_seq}, ack={client_seq + 1}')
print(f'3. Клиент -> Сервер: ACK, seq={client_seq + 1}, ack={server_seq + 1}')
print('Соединение установлено!')

Вывод:

1. Клиент -> Сервер: SYN, seq=1000
2. Сервер -> Клиент: SYN+ACK, seq=5000, ack=1001
3. Клиент -> Сервер: ACK, seq=1001, ack=5001
Соединение установлено!

Смысл шагов: SYN — «давай соединимся, мой номер 1000»; SYN+ACK — «согласен, мой номер 5000, жду твой 1001»; ACK — «подтверждаю». После этого можно слать данные. Поле ack всегда равно «следующему ожидаемому seq».

Нумерация и упорядочивание

Каждый байт в TCP пронумерован. Если сегменты придут не по порядку (а IP это допускает), приёмник соберёт их обратно по номерам:

# Сегменты пришли не по порядку — собираем по номеру
received = [(3, 'мир'), (1, 'При'), (2, 'вет, ')]
print('Пришли в порядке:', [n for n, _ in received])

ordered = sorted(received)
message = ''.join(text for _, text in ordered)
print('После сборки:     ', repr(message))

Вывод:

Пришли в порядке: [3, 1, 2]
После сборки:      'Привет, мир'

Подтверждения и повтор

Получив данные, приёмник шлёт ACK — «дошло до такого-то байта». Если отправитель не дождался ACK за отведённое время, он считает сегмент потерянным и отправляет его заново. Так TCP гарантирует доставку даже в ненадёжной сети.

Контроль потока (flow control)

Что если отправитель быстрее, чем приёмник успевает обрабатывать? Приёмник сообщает размер своего окна (window) — сколько байт он готов принять прямо сейчас. Отправитель не шлёт больше, чем влезает в окно. Это защищает медленного получателя от захлёбывания.

Контроль перегрузки (congestion control)

Отдельная задача — не перегрузить саму сеть между узлами. TCP начинает осторожно (slow start) и постепенно наращивает скорость, пока не заметит потери пакетов — тогда сбрасывает темп. Так множество TCP-соединений «делят» канал, не устраивая коллапс.

Завершение соединения

Закрывается соединение тоже упорядоченно — обменом сегментов FIN/ACK с каждой стороны (каждая сторона закрывает свою «половину» потока).

Итог

  • TCP даёт надёжный упорядоченный поток байтов поверх ненадёжного IP.
  • Соединение открывается 3-way handshake: SYN → SYN+ACK → ACK.
  • Данные нумеруются, подтверждаются ACK и при потере отправляются заново.
  • Контроль потока бережёт приёмник, контроль перегрузки — сеть.
Проверьте себя
1. Из каких шагов состоит установка TCP-соединения?
AACK → SYN → FIN
BSYN → SYN+ACK → ACK (трёхстороннее рукопожатие)
CGET → 200 OK
DPING → PONG
2. Как TCP обеспечивает правильный порядок данных?
AШифрует сегменты
BНумерует байты и собирает сегменты по номерам последовательности
CОтправляет всё одним пакетом
DИспользует MAC-адреса
3. Что делает TCP, если не получил подтверждение (ACK) на отправленный сегмент?
AЗакрывает соединение
BОтправляет сегмент заново (повторная передача)
CИгнорирует потерю
DПереключается на UDP
4. Зачем нужен контроль потока (flow control) в TCP?
AЧтобы шифровать данные
BЧтобы быстрый отправитель не захлестнул медленный приёмник (через размер окна)
CЧтобы выбрать маршрут
DЧтобы сжать данные
Поддержать проект