Практически каждое современное веб-приложение использует TCP. Когда вы открываете сайт, отправляете сообщение в мессенджере, подключаетесь по SSH или скачиваете файл - в большинстве случаев данные передаются именно через TCP (Transmission Control Protocol).
TCP - это транспортный протокол модели TCP/IP, главная задача которого - обеспечить надежную, упорядоченную и контролируемую доставку данных между двумя приложениями.
В отличие от UDP, TCP гарантирует:
Именно благодаря этим свойствам TCP стал фундаментом Интернета.
TCP в модели OSI
TCP располагается на транспортном уровне (Layer 4).
Прикладной уровень Транспортный уровень (TCP) Сетевой уровень (IP) Канальный уровень
Каждый уровень решает свою задачу.
Можно сказать, что IP - это служба доставки писем, а TCP - сотрудник, который следит, чтобы письмо дошло полностью, в правильном порядке и без потерь.
Поток байт. Почему TCP не передает сообщения
Одно из самых важных свойств TCP - он не знает ничего о сообщениях приложения. TCP работает исключительно с непрерывным потоком байт (Byte Stream).
Например, приложение отправляет:
send("Hello")
send("World")
Разработчик может ожидать, что получатель увидит два сообщения:
Hello World
На самом деле TCP гарантирует только получение последовательности байт:
HelloWorld
Получатель может прочитать данные как угодно:
HelloWorld Hel loWo rld Hello World H e lloWo rld
Границы сообщений TCP не сохраняет. Именно поэтому большинство прикладных протоколов сами определяют формат сообщений.
Обычно используется один из способов:
Сегменты TCP
Хотя приложение видит поток байт, внутри TCP данные передаются сегментами (TCP Segment).
Например, приложение отправило:
20 КБ данных
TCP разобьет поток примерно так:
1460 1460 1460 1460 ...
Каждый сегмент получает собственный TCP-заголовок. Размер сегмента определяется параметром MSS (Maximum Segment Size).
Важно понимать: Сегмент - это исключительно внутренняя единица передачи TCP. Приложение никогда не узнает, сколько сегментов использовалось.
Установка соединения
Перед передачей данных TCP выполняет процедуру Three-way Handshake.
SYN => <= SYN + ACK ACK =>
После этого соединение считается установленным.
Разберем каждый пакет.
- SYN
Клиент сообщает: Хочу установить соединение.
Также отправляется начальный номер последовательности (Initial Sequence Number).
- SYN + ACK
Сервер отвечает: Я согласен. Вот мой номер последовательности.
Одновременно подтверждает получение SYN клиента.
- ACK
Клиент подтверждает получение SYN сервера. После этого обе стороны готовы обмениваться данными.
Логика передачи сообщений
Во время работы TCP использует несколько специальных флагов.
- SYN
Используется только при установке соединения.
- ACK
Практически каждый пакет после установки соединения содержит ACK. Он подтверждает получение данных.
- FIN
Сообщает: Я больше ничего отправлять не буду.
После FIN сторона может продолжать принимать данные, но уже не отправляет новые.
- RST
Используется для аварийного завершения соединения.
Например:
Получив RST, другая сторона немедленно уничтожает соединение.
Разрыв соединения
Закрытие TCP также требует обмена сообщениями.
FIN => <= ACK <= FIN ACK =>
Получается четырехэтапное закрытие (Four-way Handshake).
Почему четыре сообщения? Потому что каждое направление соединения закрывается независимо.
TCP поддерживает полнодуплексную связь.
Нумерация данных
Каждый байт в TCP имеет собственный порядковый номер (Sequence Number).
Например, передается строка: HelloWorld
H 1 e 2 l 3 l 4 o 5 W 6 o 7 r 8 l 9 d 10
Если сегмент содержит Hello, то он имеет:
SEQ = 1 Length = 5
Следующий сегмент начинается с:
SEQ = 6
То есть номер относится к первому байту сегмента, а не к самому сегменту.
Это позволяет:
Подтверждение доставки
После получения данных принимающая сторона отправляет подтверждение.
Например:
SEQ = 1 Length = 1000
получатель отвечает:
ACK = 1001
Это означает: Все байты до 1000 включительно получены.
ACK всегда содержит номер следующего ожидаемого байта.
Формат заголовка TCP
Каждый TCP-сегмент начинается с заголовка.
Основные поля:
Минимальный размер заголовка составляет 20 байт. При наличии опций он может увеличиваться до 60 байт.
Управление потоком (Flow Control)
Быстрый отправитель может легко перегрузить медленного получателя.
Например, сервер способен принимать только 100 КБ/с, а клиент передает 10 МБ/с.
Без ограничений буфер получателя быстро переполнится. Для решения этой проблемы TCP использует механизм Flow Control.
Получатель сообщает: Window = 32768
Это означает: Я сейчас готов принять еще 32768 байт.
После заполнения буфера окно уменьшается. Когда буфер освобождается, окно снова увеличивается.
Если окно становится равным нулю, отправитель временно прекращает передачу данных и периодически отправляет специальные probe-пакеты, чтобы узнать, освободилось ли место.
Важно понимать, что Flow Control и Congestion Control - разные механизмы:
Source: Orkhan Alishov's notes