Протокол TCP: как устроены соединение, заголовок и флаги

TCP доставляет данные от программы к программе целиком и по порядку: открывает соединение тройным рукопожатием, нумерует байты и повторяет потерянное.

Разбираем заголовок сегмента и восемь флагов, окна и CUBIC, запускаем TCP-сервер на Python и смотрим, откуда берутся Connection refused и TIME_WAIT.

Статью написал:
Ваня Буявец, продюсер, основатель Checkroi
Ваня Буявец
Основатель Checkroi, продюсер, эксперт в выборе онлайн-курсов
Все 3192 статьи автора Подписаться на Телеграм-канал
Одобрено экспертом:
Наташа Буявец, основатель Checkroi, эксперт по онлайн-курсам
Наташа Буявец
Основательница Checkroi, продюсер Youtube-каналов, эксперт по онлайн-курсам
Все 3846 экспертных мнений Подписаться на Телеграм-канал
Обложка: Протокол TCP: как устроены соединение, заголовок и флаги

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

Читается аббревиатура «ти-си-пи». В заголовке IP-пакета у TCP номер 6, у UDP номер 17, оба есть в реестре номеров протоколов IANA. Действующая спецификация протокола описана в RFC 9293 от августа 2022 года: в него собраны все поправки к RFC 793 1981 года. На TCP работают сайты, почта, SSH и большинство баз данных. Где TCP стоит среди других протоколов, мы разбирали в статье о сетевых протоколах простыми словами.

CheckroiCheckroiПодборка курсов по сетевому администрированию40 курсов • 18 школСравните цены, школы, программу и найдите выгодные предложения по обучениюСравнить→

Что такое TCP простыми словами

Рой нумерует конверты со страницами книги

Представьте, что вы отправляете книгу по почте, но по одной странице в конверте. Конверты едут разными машинами: часть приходит раньше, часть теряется. TCP работает как служба, которая нумерует конверты, раскладывает страницы на месте по порядку и просит прислать заново потерянные.

Для программы это выглядит как сплошной поток байтов. Браузер пишет запрос в сокет и читает ответ, а как этот ответ резали на куски и сколько раз переотправляли, его не касается. Что такое сокет и как через него открывается соединение, есть в отдельной статье про сокеты.

Что TCP обеспечивает:

  • соединение: прежде чем отправить данные, стороны договариваются о начале связи;
  • порядок: каждый байт получает порядковый номер, и получатель собирает их правильно;
  • подтверждения: получатель сообщает, до какого байта всё дошло;
  • повторную отправку: неподтверждённое отправитель шлёт заново;
  • управление потоком: быстрый отправитель не заваливает медленного получателя;
  • управление перегрузкой: отправитель сбавляет скорость, когда сеть начинает терять пакеты.

TCP работает на транспортном уровне. Ниже него IP, который доставляет пакеты между компьютерами, выше прикладные протоколы вроде HTTP. По модели OSI это четвёртый уровень из семи, подробная раскладка по уровням есть в разборе модели OSI.

Уровень стека TCP/IP Что делает Примеры протоколов
Прикладной Смысл данных: страница, письмо, команда HTTP, SMTP, SSH, DNS
Транспортный Доставка от программы к программе по портам TCP, UDP
Межсетевой Доставка от компьютера к компьютеру по IP-адресам IPv4, IPv6, ICMP
Канальный Передача кадров внутри одной сети Ethernet, Wi-Fi

Как работает протокол TCP

Рой пожимает руку собеседнику через экран ноутбука

У каждого TCP-соединения три фазы: установка, передача данных и завершение. Программы видят их как вызовы connect(), send() и close(), а внутри каждая фаза состоит из обмена служебными сегментами.

Установка соединения: тройное рукопожатие

Соединение открывается за три сегмента, поэтому процедуру называют трёхсторонним рукопожатием (three-way handshake). Каждая сторона выбирает свой начальный порядковый номер, ISN, и сообщает его другой.

Шаг Кто отправляет Флаги Порядковый номер Номер подтверждения
1 Клиент SYN 1000 —
2 Сервер SYN, ACK 5000 1001
3 Клиент ACK 1001 5001

Числа в таблице условные: настоящие ISN выбираются случайно, чтобы постороннему было трудно их угадать. Номер подтверждения всегда на единицу больше чужого ISN, потому что флаг SYN занимает один порядковый номер. После третьего сегмента обе стороны переходят в состояние ESTABLISHED и начинают обмен данными.

Зачем три шага, а не два? Так стороны убеждаются, что слышат друг друга в обе стороны, и отсеивают старые запросы на соединение, застрявшие в сети. Главной причиной спецификация TCP называет именно это: рукопожатие защищает от путаницы со старыми дублями запросов на соединение.

Передача данных и подтверждения

Данные идут сегментами. В каждом указан номер первого байта, а получатель отвечает сегментом с флагом ACK и номером следующего байта, который ждёт. Подтверждение накопительное: ACK с номером 7001 означает «всё до 7000 включительно дошло».

Если подтверждение не пришло за время RTO (retransmission timeout), отправитель повторяет сегмент. RTO подстраивается под измеренное время ответа сети, то есть под пинг, а до первых замеров начать с 1 секунды рекомендует RFC 6298. Пропавший сегмент обнаруживается и раньше таймера: три дублирующих ACK подряд подсказывают отправителю, что получатель видит дыру.

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

Закрывается соединение по частям. Сторона, которой больше нечего отправить, посылает FIN, вторая подтверждает его и может ещё досылать свои данные. Потом вторая сторона отправляет свой FIN и получает ACK.

  1. Клиент шлёт FIN: «я закончил отправлять».
  2. Сервер отвечает ACK: «понял».
  3. Сервер шлёт свой FIN: «я тоже закончил».
  4. Клиент отвечает ACK и переходит в состояние TIME_WAIT.

TIME_WAIT длится два MSL (Maximum Segment Lifetime, максимальное время жизни сегмента в сети). Пауза нужна, чтобы запоздавшие сегменты старого соединения не попали в новое с теми же портами. В спецификации TCP MSL принят за 2 минуты, а системы ставят своё значение: на macOS команда sysctl net.inet.tcp.msl выводит 15000, то есть 15 секунд.

Есть и аварийный выход, флаг RST. Им сбрасывают соединение без вежливого FIN: так отвечает закрытый порт, так рвёт связь программа, которая упала или решила, что собеседник шлёт ерунду.

Канал основателя Checkroi Вани Буявца3 800 человек читают мой Телеграм-канал про нейросетиСобрал промпты для Claude Code и ChatGPT, разборы ИИ-инструментов и лайфхаки по продвижению бизнеса в одном местеПрисоединиться

Структура TCP-пакета: заголовок и флаги

Конверт-сегмент с подписанными полями заголовка

Единица данных TCP называется сегментом. Сегмент едет по сети внутри IP-пакета, поэтому в разговоре его тоже зовут «TCP-пакетом», и это понятно всем. Сегмент состоит из заголовка и данных, заголовок без опций занимает 20 байт.

Поле Размер Зачем нужно
Порт источника 16 бит Какая программа отправила сегмент
Порт назначения 16 бит Какой программе он адресован
Порядковый номер (Sequence Number) 32 бита Номер первого байта данных в сегменте
Номер подтверждения (Acknowledgment Number) 32 бита Какой байт получатель ждёт следующим
Длина заголовка (Data Offset) 4 бита Где кончается заголовок и начинаются данные, в 32-битных словах
Зарезервировано 4 бита Под будущие флаги, сейчас нули
Флаги 8 бит Служебные отметки: SYN, ACK, FIN и другие
Размер окна (Window) 16 бит Сколько байт получатель готов принять
Контрольная сумма 16 бит Проверка, что сегмент не испорчен
Указатель срочности 16 бит Где кончаются срочные данные, если стоит URG
Опции 0–40 байт MSS, масштаб окна, метки времени, выборочные подтверждения

Поле длины заголовка четырёхбитное, максимум 15 слов по 4 байта, поэтому заголовок с опциями не бывает больше 60 байт. Порт занимает 16 бит, отсюда диапазон от 0 до 65535. Какие номера портов за чем закреплены, разобрано в статье о портах компьютера.

Флаги TCP

Флагов восемь, каждый занимает один бит. Список ведёт IANA, а в RFC 9293 перечислены текущие: CWR, ECE, URG, ACK, PSH, RST, SYN и FIN.

Флаг Расшифровка Когда встречается
SYN Synchronize Открытие соединения, первые два сегмента рукопожатия
ACK Acknowledgment Почти в каждом сегменте после первого
FIN Finish Сторона закончила отправку
RST Reset Аварийный сброс, ответ закрытого порта
PSH Push Просьба сразу отдать данные программе, не копить в буфере
URG Urgent Есть срочные данные, сейчас почти не используется
ECE ECN-Echo Получатель сообщает, что роутер пометил перегрузку
CWR Congestion Window Reduced Отправитель отвечает, что уже сбавил скорость

Два последних флага добавил RFC 3168 о явном уведомлении о перегрузке, ECN. С ним роутер не выбрасывает пакет при заторе, а помечает его, и TCP снижает скорость без потерь.

MSS и размер окна

MSS (Maximum Segment Size) показывает, сколько байт данных влезает в один сегмент. Стороны сообщают его друг другу опцией в SYN. В обычной сети Ethernet пакет ограничен 1500 байтами, минус 20 байт IP-заголовка и 20 байт TCP-заголовка, получается 1460. Если опции нет, отправитель обязан считать MSS равным 536 байтам для IPv4.

Поле окна шестнадцатибитное, без расширений это максимум 65 535 байт. Для быстрых каналов мало, поэтому есть опция масштаба окна из RFC 7323: значение сдвигается на число бит, указанное при рукопожатии, и окно растёт до 1 ГиБ.

CheckroiCheckroiПодборка курсов по Linux158 курсов • 30 школСравните цены, школы, программу и найдите выгодные предложения по обучениюСравнить→

Управление потоком и перегрузкой

Скорость TCP ограничивают два окна, и отправитель смотрит на меньшее из них.

  • Окно получателя (rwnd) приходит в каждом сегменте в поле Window. Это место в буфере принимающей программы. Если программа не успевает читать, окно сжимается до нуля, и отправитель ждёт.
  • Окно перегрузки (cwnd) отправитель считает сам по тому, как сеть доставляет пакеты. Его никто не передаёт, это оценка пропускной способности пути.

Новое соединение стартует осторожно. По RFC 6928 начальное окно составляет до 10 сегментов, около 14,6 КБ. Дальше идёт медленный старт: с каждым подтверждением окно растёт, за каждый круг примерно вдвое. При первой потере рост переходит в плавный.

Как именно окно растёт и падает, решает алгоритм управления перегрузкой. Самый распространённый из них CUBIC: в RFC 9438 сказано, что его приняли алгоритмом по умолчанию в Linux, Windows и стеках Apple. Проверить на Linux-сервере можно так:

$ sysctl net.ipv4.tcp_congestion_control
net.ipv4.tcp_congestion_control = cubic

Так команда отвечает на Ubuntu. Вместо CUBIC можно включить, например, BBR от Google, если ядро собрано с ним, но для большинства задач менять ничего не нужно.

Чем TCP отличается от UDP

UDP отправляет датаграммы без соединения, без нумерации и без подтверждений. Он быстрее начинает и не ждёт потерянного, зато за порядок и целостность отвечает уже сама программа.

Свойство TCP UDP
Соединение Да, тройное рукопожатие Нет
Гарантия доставки Да, повтор потерянного Нет
Порядок данных Сохраняется Не гарантирован
Минимальный заголовок 20 байт 8 байт
Номер протокола в IP 6 17
Где используют Сайты, почта, SSH, базы данных DNS-запросы, звонки, игры, видео в реальном времени

Подробное сравнение, пример UDP-сервера и разбор, почему HTTP/3 ушёл на UDP, есть в статье о протоколе UDP.

Что ходит по TCP и на каких портах это встречается чаще всего:

  • HTTP и HTTPS, порты 80 и 443: страницы сайтов. Как устроены запрос и ответ, разобрано в статье о HTTP, а шифрование поверх TCP разобрано в материале про SSL и TLS.
  • SSH, порт 22: удалённое управление сервером.
  • SMTP, IMAP, POP3: почта, порты и настройки собраны в разборе почтовых протоколов.
  • Базы данных: MySQL на порту 3306, PostgreSQL на 5432.
  • DNS: обычно UDP 53, но длинные ответы и передача зон идут по TCP 53, подробнее в статье о работе DNS.
CheckroiCheckroiПодборка курсов по PostgreSQL158 курсов • 27 школСравните цены, школы, программу и найдите выгодные предложения по обучениюСравнить→

Как увидеть TCP-соединение своими глазами

Проще всего поднять сервер и клиента на своём компьютере. Нужен только Python 3, библиотека socket входит в стандартную поставку. Если Python пока в новинку, начните с простых примеров кода для новичков.

CheckroiCheckroiПодборка курсов по Python569 курсов • 52 школыСравните цены, школы, программу и найдите выгодные предложения по обучениюСравнить→

Сервер, файл tcp_server.py:

import socket

with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as srv:
    srv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    srv.bind(("127.0.0.1", 5050))
    srv.listen()
    print("Жду подключения на порту 5050")
    conn, addr = srv.accept()
    with conn:
        print("Подключился клиент", addr)
        data = conn.recv(1024)
        print("Получено:", data.decode())
        conn.sendall(data.upper())

Клиент, файл tcp_client.py:

import socket

with socket.create_connection(("127.0.0.1", 5050), timeout=5) as sock:
    sock.sendall("привет, tcp".encode())
    print("Ответ сервера:", sock.recv(1024).decode())

SOCK_STREAM в коде и означает TCP, для UDP был бы SOCK_DGRAM. Запускаем сервер в одном окне терминала, клиента в другом. Вывод сервера:

Жду подключения на порту 5050
Подключился клиент ('127.0.0.1', 59451)
Получено: привет, tcp

Вывод клиента:

Ответ сервера: привет, TCP

Обратите внимание: заглавными стали только латинские буквы. Сервер вызывает upper() у байтов, а не у строки, и такой метод знает только ASCII. TCP доставил байты как есть, а что с ними делать, решает программа.

Рукопожатие прошло внутри create_connection(), завершение при выходе из блоков with. Порт клиента 59451 система выдала сама из временного диапазона. Сразу после обмена посмотрим состояние соединений на macOS:

$ netstat -an -p tcp | grep 5050
tcp4       0      0  127.0.0.1.5050         127.0.0.1.59451        TIME_WAIT

Сервер закрыл соединение первым, поэтому TIME_WAIT остался на его стороне. Через полминуты строка исчезнет.

Чем смотреть соединения в разных системах:

  • Linux: ss -tan показывает все TCP-сокеты с состоянием, очередями приёма и отправки, адресами и портами.
  • macOS: netstat -an -p tcp, как в примере выше.
  • Windows: netstat -an -p TCP в командной строке.
  • Wireshark показывает сами сегменты с флагами и номерами. Фильтр tcp.flags.syn == 1 оставит только открытия соединений, все поля описаны в справочнике фильтров Wireshark.
CheckroiCheckroiПодборка курсов по Wireshark47 курсов • 17 школСравните цены, школы, программу и найдите выгодные предложения по обучениюСравнить→ Ваня БуявецКанал основателя Checkroi Вани БуявцаЗабирайте промпты и обучение по нейросетям в моём Телеграм-каналеБольше 3 800 человек уже применяют Claude Code, ChatGPT и другие нейросети в работе, учёбе, бизнесе и жизниПерейти в канал

Частые ошибки и что с ними делать

За почти любой сетевой ошибкой в логах стоит шаг TCP, который пошёл не так. По тексту ошибки видно, где именно оборвалось.

Connection refused

Попробуем подключиться к порту 5051, где никто не слушает:

import socket

try:
    socket.create_connection(("127.0.0.1", 5051), timeout=3)
except ConnectionRefusedError as e:
    print("Ошибка:", e)
Ошибка: [Errno 61] Connection refused

Это вывод на macOS, в Linux номер ошибки 111. Компьютер ответил на SYN сегментом RST, то есть машина доступна, но на порту нет программы.

Решение: проверить, что сервис запущен и слушает нужный адрес. Частая ловушка: сервер привязан к 127.0.0.1, а подключаются снаружи. Тогда нужно слушать 0.0.0.0 или конкретный внешний адрес.

Connection timed out

На SYN не пришло ничего. Обычно пакет молча отбрасывает межсетевой экран или адрес недоступен. Linux по умолчанию повторяет SYN 6 раз, это примерно 127 секунд ожидания, так описан параметр tcp_syn_retries в руководстве tcp(7).

Решение: проверить правила файрвола на сервере и по пути, открыт ли порт у хостинга, правильный ли адрес. Если Connection refused приходит мгновенно, а timeout тянется минуту, до машины пакеты идут, но их кто-то фильтрует.

Connection reset by peer

Соединение уже работало, и вдруг пришёл RST. Собеседник упал, перезапустился, закрыл сокет, не дочитав данные, или промежуточный балансировщик оборвал простаивающее соединение. Разбор причин на примере Java есть в статье про ошибку Connection reset.

Address already in use

Сервер перезапустили, а порт занят: на нём висят соединения в TIME_WAIT от прошлого запуска. Решение: опция SO_REUSEADDR перед bind(), она стоит в нашем примере сервера. Если ошибка не уходит, порт держит другой живой процесс: на Linux его покажет ss -ltnp.

Много соединений в CLOSE_WAIT

CLOSE_WAIT значит, что собеседник прислал FIN, а ваша программа так и не вызвала close(). Сеть здесь в порядке, сокеты утекают в коде.

Решение: искать место, где соединение открывается и не закрывается при ошибке. В Python для этого и нужен блок with.

Где освоить сети и TCP/IP системно

Чтобы прочитать заголовок сегмента и понять, откуда взялся TIME_WAIT, хватит этой статьи и одного вечера с Wireshark. Чтобы проектировать сеть, настраивать маршрутизацию и искать, где теряются пакеты между офисами, нужна системная подготовка. Её дают курсы по сетевому администрированию: TCP/IP там проходят вместе с маршрутизацией, VLAN и диагностикой.

TCP на уровне пакетов разбирают в работе системного администратора и сетевого инженера. Ниже курсы, на которых этому учат:

КурсШколаСтоимость со скидкойВ рассрочкуДлитель­ностьОбзор курса от Checkroi
Администрирование ОС Linux
Перейти на сайт курса
SkillboxSkillbox87 595 ₽5556 ₽/мес.3 месяцаОбзор курса
Профессия «Сетевой инженер»
Перейти на сайт курса
НетологияНетология85 800 ₽3575 ₽/мес.12 месяцевОбзор курса
Сетевые технологии
Перейти на сайт курса
MerionMerion21 840 ₽1578 ₽/мес.2 месяцаОбзор курса
Сетевые технологии Huawei
Перейти на сайт курса
MerionMerion11 830 ₽938 ₽/мес.2 месяцаОбзор курса
Системное администрирование и информационные технологии - курс переподготовки
Перейти на сайт курса
АПОК — Академия профессионального образования кадровАПОК32 980 ₽2748 ₽/мес.576 часовОбзор курса

Больше программ — в полном каталоге курсов по сетевому администрированию

Часто задаваемые вопросы

Как расшифровывается и читается TCP?

Transmission Control Protocol, по-русски «протокол управления передачей». Читается «ти-си-пи». В поиске часто пишут кириллицей «тср» или «тсп», имеют в виду тот же протокол.

Какой номер у протокола TCP?

В заголовке IP-пакета у TCP номер 6, у UDP номер 17. Номера закреплены в реестре IANA. Не путайте его с номером порта: порт выбирает программа, а номер протокола говорит IP, что внутри пакета лежит TCP-сегмент.

На каком уровне модели OSI работает TCP?

На четвёртом, транспортном. Ниже него сетевой уровень с IP, выше прикладные протоколы вроде HTTP, SMTP и SSH. В стеке TCP/IP этот уровень тоже называется транспортным.

Чем TCP-сегмент отличается от пакета?

Сегмент состоит из TCP-заголовка и данных. Пакетом называют IP-пакет, внутри которого сегмент едет по сети. В разговоре оба слова смешивают, и «TCP-пакет» понимают все.

Сколько весит заголовок TCP?

Без опций 20 байт, с опциями до 60 байт. Предел задаёт четырёхбитное поле длины заголовка: в нём помещается максимум 15 слов по 4 байта.

Что значат флаги SYN, ACK, FIN и RST?

SYN открывает соединение, ACK подтверждает полученные данные, FIN сообщает, что сторона закончила отправку, а RST сбрасывает соединение аварийно. Всего флагов восемь, ещё PSH, URG, ECE и CWR.

Читайте Checkroi первым в Google
Добавьте Checkroi в избранные источники — и наши разборы курсов и обзоры школ будут показываться выше в вашей выдаче Google.
Оставить комментарий
0 комментариев
Форма комментария

Оставьте комментарий

Напишите, что думаете. Нам важно ваше мнение!