TCP
| Полное название | Transmission Control Protocol |
|---|---|
| Авторы | Винтон Серф, Роберт Кан |
| Спецификация | RFC 793 (1981), актуальная — RFC 9293 (2022) |
| Уровень | транспортный (модели OSI и TCP/IP) |
| Гарантии | надёжность, порядок, целостность, управление потоком и перегрузкой |
| Установление соединения | тройное рукопожатие (SYN, SYN-ACK, ACK) |
| Алгоритмы перегрузки | Reno, CUBIC (по умолчанию в Linux), BBR |
TCP (англ. Transmission Control Protocol — протокол управления передачей) — транспортный протокол стека TCP/IP, который превращает ненадёжную доставку пакетов протоколом IP в надёжный упорядоченный поток байтов между двумя программами. TCP гарантирует, что данные дойдут полностью, без искажений и в том порядке, в каком были отправлены, а скорость передачи подстроится под возможности получателя и сети.
Поверх TCP работают HTTP/1.1 и HTTP/2, TLS, SSH, почтовые протоколы, протоколы баз данных — большая часть трафика интернета. Альтернатива для задач, где важнее скорость, чем гарантии, — UDP.
История
Винтон Серф и Роберт Кан описали протокол в 1974 году; позже его разделили на IP (маршрутизация) и TCP (надёжная доставка). Спецификация вышла как RFC 793 в 1981 году, а в 2022 году её переработали и объединили с последующими исправлениями в RFC 9293.[1] В 1986 году интернет пережил серию «коллапсов перегрузки», когда полезная пропускная способность падала в тысячу раз; Ван Якобсон в 1988 году предложил алгоритмы управления перегрузкой, которые спасли сеть и с изменениями используются до сих пор.[2]
Установление и завершение соединения
TCP — протокол с установлением соединения. Перед передачей данных стороны обмениваются тремя сегментами — тройное рукопожатие:
Клиент Сервер
| ------ SYN (seq=x) ---------> |
| <---- SYN-ACK (seq=y, ack=x+1) |
| ------ ACK (ack=y+1) -------> |
| соединение установлено |
Рукопожатие занимает один круг «туда-обратно» (RTT) до отправки первых данных, а TLS поверх TCP добавляет ещё один круг. Завершение — обмен сегментами FIN и ACK с каждой стороны; сторона, закрывшая соединение первой, проводит некоторое время в состоянии TIME_WAIT, чтобы опоздавшие пакеты не попали в новое соединение. Сегмент RST обрывает соединение немедленно — например, при попытке подключиться к закрытому порту.
Надёжность и порядок
- Номера последовательности. Каждый байт потока имеет номер; получатель собирает данные по порядку, даже если пакеты пришли вразнобой, и отбрасывает дубликаты.
- Подтверждения. Получатель сообщает, до какого байта всё получено (ACK). Неподтверждённые данные отправитель повторяет по таймауту или после трёх повторных подтверждений (fast retransmit).
- Контрольная сумма выявляет повреждённые сегменты.
Управление потоком и перегрузкой
Управление потоком защищает получателя: в каждом подтверждении он сообщает размер окна — сколько ещё байтов готов принять. Медленный получатель автоматически притормаживает быстрого отправителя.
Управление перегрузкой защищает сеть. Отправитель держит окно перегрузки и увеличивает его, пока сеть справляется: сначала экспоненциально (медленный старт), затем линейно. Потеря пакета воспринимается как сигнал перегрузки, и окно резко уменьшается. Алгоритмы:
| Алгоритм | Сигнал | Где используется |
|---|---|---|
| Reno, NewReno | потери пакетов | классика, учебники |
| CUBIC | потери; рост окна по кубической функции времени | по умолчанию в Linux, Windows, macOS[3] |
| BBR | оценка пропускной способности и минимальной задержки | Google, YouTube; полезен на каналах с потерями[4] |
sysctl net.ipv4.tcp_congestion_control # текущий алгоритм в Linux
sudo sysctl -w net.ipv4.tcp_congestion_control=bbr # включить BBR
Порты и сокеты
Соединение однозначно определяется четвёркой: IP-адрес и порт клиента, IP-адрес и порт сервера. Сервер слушает известный порт (443 для HTTPS, 22 для SSH, 5432 для PostgreSQL), а клиент получает случайный временный порт.
ss -tlnp # слушающие TCP-сокеты и процессы
ss -tn state established # установленные соединения
ss -s # сводка, включая TIME_WAIT
nc -vz example.com 443 # проверить, открыт ли порт
Утилита ss — современная замена netstat.[6]
Проблемы и оптимизации
- Блокировка начала очереди (head-of-line blocking). Поток строго упорядочен: потеря одного пакета задерживает все последующие данные, даже относящиеся к другим запросам. В HTTP/2 это тормозит все параллельные потоки — главная причина появления QUIC.
- Задержка установления. Рукопожатие TCP плюс TLS — два круга до первого байта данных. TCP Fast Open позволяет отправлять данные уже в SYN при повторных соединениях,[5] TLS 1.3 сокращает своё рукопожатие до одного круга.
- Алгоритм Нейгла склеивает мелкие записи в один сегмент, экономя трафик, но добавляя задержку; интерактивные приложения отключают его опцией
TCP_NODELAY. - Keepalive. Простаивающие соединения могут обрываться промежуточными устройствами (NAT, файрволы); периодические проверки поддерживают их и выявляют «мёртвые» соединения.
TCP и приложения
Для программиста TCP — это сокет, в который пишут и из которого читают поток байтов. Важно помнить, что это поток, а не сообщения: один вызов send не обязательно соответствует одному recv на другой стороне. Протокол приложения должен сам разделять сообщения — длиной в заголовке, разделителями или, как в HTTP, заголовком Content-Length.