Протокол TCP является одним из важнейших протоколов связи в компьютерных сетях. В этой статье познакомимся с ним поближе.
Что такое транспортные протоколы
Транспортные протоколы (TCP и UDP) используются для передачи информации. Информация передаётся маленькими частями — сетевыми пакетами. То есть поток информации разбивается на много маленьких пакетов.
Каждый пакет состоит из заголовка и самих данных. Заголовок содержит служебную информацию, например порт источника и назначения.
Спецификация доступна здесь, но мне кажется мало кто осилит это чтение.
Протокол TCP и его особенности
Протокол TCP находится на 4 уровне модели OSI.
Главной особенностью TCP (Transmission Control Protocol) является то, что этот протокол гарантирует доставку всех отправленных пакетов. При этом проверяется целостность пакетов и их порядок. Если пакет потерялся или испортился, то получатель запросит эти пакеты у отправителя снова. Если пакеты пришли не в том порядке, то они на принимающей стороне всё равно обработаются в правильном. Этот механизм контроля доставки накладывает дополнительную нагрузку в виде увеличения служебной информации, которую нужно передать вместе с полезными данными.
Протокол TCP делит поток информации на сегменты. В одном сегменте может быть несколько пакетов. Каждый сегмент проверяется на целостность, и если все хорошо, отправляется подтверждение передающей стороне. Таким образом подтверждается не каждый пакет, а каждый сегмент, но в сегменте может оказаться и всего лишь один пакет.
Поверх протокола TCP работают многие прикладные протоколы:
- сайты (http, https);
- электронная почта (imap, pop, smtp);
- передача файлов (cifs, nfs, ftp);
- удаленные подключения (rdp, ssh).
TCP пакеты передаются не просто так, а в рамках установленного соединения — которое называют TCP сессией.
Подключение можно выполнить только если вторая сторона прослушивает порт, к которому будет выполняться подключение.
Алгоритм работы TCP
Алгоритм работы TCP следующий:
- Используя трехкратное рукопожатие, между двумя узлами создаётся сеанс связи.
- При отправке пакетов узлы последовательно нумеруют их и рассчитывают контрольную сумму.
- Поскольку все пакеты имеют последовательные номера, то становится видно если какие-то из них отсутствуют. В этом случае отправляется запрос на повторную отправку пакета.
- Если для какого-то пакета не совпала контрольная сумма, то отправляется запрос на повторную отправку пакета.
При открытии даже одной веб странички создаются несколько TCP соединений для:
- html страницы;
- каждого CSS и JavaScript файлов;
- каждого изображения.
И для каждого такого соединения вначале устанавливается сеанс, что замедляет передачу данных.
Заголовок TCP пакета
Заголовок TCP пакета состоит из следующих полей:
- Порт отправителя.
- Порт получателя.
- Порядковый номер в сегменте (sequence number). В целях безопасности номер первого пакета в сегменте генерируется случайным образом и может быть равно от 0 до 4294967295. Следующие пакеты, в отправляемом сегменте, просто увеличиваю свой порядковый номер;
- Номер подтверждения (acknowledgment number). Когда мы подтверждаем определённый пакет, в нем записывается sequence number подтверждаемого пакета.
- Длина заголовка (data offset). В этом поле указывается длина заголовка TCP пакета и где начинаются фактические данные.
- Зарезервированное поле. Эти биты зарезервированы для будущего использования.
- Флаги. Необходимы для дополнительной функциональности. Например, позволяют установить или разорвать соединение, включить или выключить защиту от перегрузки сети и тому подобное.
- Размер окна (Window Size). Указывается количество байт, считая от последнего номера подтверждения, которые готов принять отправитель данного пакета. То есть, какой у него в данный момент времени размер буфера.
- Контрольная сумма (Checksum). Используется для проверки на наличие ошибок при приеме или передачи пакетов. Рассчитывается с учетом заголовка (кроме контрольной суммы) и самих данных.
- Указатель срочности (Urgent pointer). Используется, если стоит флаг URG. По этому значению определяются срочные данные и они сразу же передаются приложению. Остальные данные попадают в буфер.
- Дополнительные опции. Необязательно, но используются почти всегда.
- Заполнение (Padding). Дополняет заголовок, пока он не закончится на 32-разрядной границе. Всегда состоит только из нулей.
Флаги в заголовке TCP
- NS (Nonce Sum). Защита от случайного или злонамеренного изменения флагов. Используется для улучшения работы механизма явного уведомления о перегрузке ECN (Explicit Congestion Notification).
- CWR (Congestion Window Reduced). Подтверждение получения пакета с флагом ECE и включением механизма уменьшения перегрузки (Congestion Control). Этот механизм позволяет оптимизировать отправку пакетов в перегруженных сетях.
- ECE (ECN-Echo). Выполняет две функции. Если соединение только устанавливается, то означает что отправитель поддерживает ECN. В другом случае, это означает перегрузку сети (или предстоящую перегрузку) для отправителя.
- URG (Urgent). Указатель важности. 0 если не используется, 1 — используется.
- ACK. Устанавливается, когда принимающая сторона подтверждает полученный пакет. Чтобы отправитель знал, какие пакеты уже были доставлены получателю. При этом в поле acknowledgment number записывается номер подтверждаемого пакета.
- PSH (Push). Обычно получатель не подтверждает каждый пакет при получении. Вместо этого пакеты накапливаются в буфере, пока не передадутся приложению. Данный флаг сообщает получателю, что нужно немедленно передать всё из буфера приложению и сразу же отправить подтверждение.
- RST. Сообщает о немедленном разрыве соединения. При этом соединение обрывается, а буфер очищается. Самые распространенные причины отправки пакета с таким флагом:
- ответ на пакет, полученный для закрытого сокета;
- пользователь сам прервал соединение (например, закрыв браузер, не дожидаясь ответа);
- соединение не было нормально закрыто, но находится в неактивном состоянии некоторое время.
- SYN. Данный флаг означает начало соединения. Он также синхронизирует начальные номера. Первый пакет, отправленный с каждой стороны, должен иметь этот флаг.
- FIN. Сообщает другой стороне что все пакеты были отправлены, и соединение пора завершить.
Создание TCP сессии
Для установления соединения использует трехкратное рукопожатие.
Первый этап. Клиент отправляет на сервер пакет с флагом SYN. При этом клиент устанавливает порядковый номер сегмента на случайное значение A.
Второй этап. В ответ сервер отвечает пакетом с флагами SYN и ACK. Номер подтверждения установлен на единицу больше принятого (A+1). Поскольку сервер также будет отправлять данные, то для себя он тоже выбирает номер первого пакета, который будет другим случайным числом B.
Третий этап. Клиент отправляет ACK на сервер. Порядковый номер устанавливается равным A+1, а номер подтверждения устанавливается на B+1.
На этом этапе клиент и сервер получили подтверждение соединения и образовали двухстороннюю связь.
Передача данных в TCP
Теперь разберём пример передачи данных в уже установленном сеансе.
Клиент отравляет запрос к серверу. Поскольку данные поместились в один пакет TCP, то этот запрос (пакет) получил флаг PSH, чтобы сервер не ждал продолжение получения данных. При этом пакет получил 2 флага: ACK (подтвердил предыдущею передачу пакетов от сервера) и PSH.
В ответ на это сервер отправляет пакет ACK с номером успешно полученных данных.
Далее сервер обработал запрос и отправляет данные клиенту. Эти данные делятся на пакеты и отправляются сегментами.
Далее клиент подтверждает, что данные получены отправляя пакеты с флагом ACK.
Завершение сеанса TCP
Завершение сеанса использует четырёхкратное рукопожатие, причём каждая сторона завершает своё соединение независимо.
Когда одна из сторон хочет остановить свою половину соединения, она передаёт пакет FIN, который другая сторона подтверждает пакетом с ACK.
После того, как сторона, отправившая первый FIN, ответила с последним ACK, она ожидает некоторое время прежде чем окончательно закрыть соединение. В течение этого времени локальный порт недоступен для новых соединений.
Соединение может быть «полуоткрытым», и в этом случае одна сторона завершила свою часть, а другая — нет. Завершившая сторона больше не может отправлять какие-либо данные, но другая сторона может. Завершающая сторона должна продолжить чтение данных, пока другая сторона также не завершит свою работу.
Также возможно разорвать соединение трёхкратным рукопожатием, когда первая сторона отправляет FIN, а вторая отвечает FIN и ACK (просто объединяет 2 шага в один). Дальше первая сторона подтверждает завершение сеанса с помощью ACK.
Состояния сеанса TCP
Сеанс TCP может находится в следующих состояниях:
- CLOSED — начальное состояние;
- LISTEN — сервер ожидает запросы от клиента;
- SYN-SENT — клиент хочет установить соединение с сервером и ожидает подтверждение;
- SYN-RECEIVED — сервер получил запрос на создание сеанса, отправил ответный запрос и ожидает подтверждение;
- ESTABLISHED — соединение установлено, идёт передача данных;
- FIN-WAIT-1 — одна из сторон завершает соединение, отправив флаг FIN;
- CLOSE-WAIT — другая сторона переходит в это состояние, отправив подтверждение на FIN, но продолжает передачу;
- FIN-WAIT-2 — первый узел получил ACK, разорвал свое соединение, но еще читает данные;
- LAST-ACK — второй узел заканчивает передачу и отправляет флаг FIN;
- TIME-WAIT — сервер получил пакет с флагом FIN, отправил флаг ACK и ждёт некоторое время, перед окончательным закрытием соединения;
- CLOSING — обе стороны инициировали закрытие соединения одновременно.
Итог
Вот мы и познакомились с одним из самых важных протоколов сети Интернет. Протокол TCP действительно очень важен в компьютерных сетях. Разобрались с его особенностями, алгоритмом работы. Узнали про сеансы TCP, пакеты и сегменты.
Я также описывал ещё один важный протокол компьютерной сети, а именно протокол UDP. Статья доступна здесь.