Протокол TCP. Разбираем по косточкам компьютерные сети: HTTP, TCP, REST

0 - 3

4 - 9

10 - 15

16 - 31

Порт источника, Source Port

Порт назначения, Destination Port

Порядковый номер, Sequence Number (SN)

Номер подтверждения,

Длина заголовка

Зарезервировано

Флаги

Размер Окна

Контрольная сумма

Указатель важности

Опции (необязательное, но используется практически всегда)

160/192+

Данные

П орт источника, Порт назначения

Эти 16-битные поля содержат номера портов - числа, которые определяются по специальному списку .

Порт источника идентифицирует приложение клиента, с которого отправлены пакеты. Ответные данные передаются клиенту на основании этого номера.

Порт назначения идентифицирует порт, на который отправлен пакет.

П орядковый номер

Порядковый номер выполняет две задачи:

  1. Если установлен флаг SYN, то это изначальный порядковый номер - ISN (Initial Sequence Number), и первый байт данных, которые будут переданы в следующем пакете, будет иметь номер, равный ISN + 1.
  2. В противном случае, если SYN не установлен, первый байт данных, передаваемый в данном пакете, имеет этот порядковый номер

Поскольку поток TCP в общем случае может быть длиннее, чем число различных состояний этого поля, то все операции с порядковым номером должны выполняться по модулю 2 32 . Это накладывает практическое ограничение на использование TCP. Если скорость передачи коммуникационной системы такова, чтобы в течение MSL (максимального времени жизни сегмента) произошло переполнение порядкового номера, то в сети может появиться два сегмента с одинаковым номером, относящихся к разным частям потока, и приёмник получит некорректные данные.

Н омер подтверждения

Acknowledgment Number (ACK SN) (32 бита) - если установлен бит ACK, то это поле содержит порядковый номер октета, который отправитель данного сегмента желает получить. Это означает, что все предыдущие октеты (с номерами от ISN+1 до ACK-1 включительно) были успешно получены.

Д лина заголовка (смещение данных)

Это поле определяет размер заголовка пакета TCP в 4-байтных (4-октетных) словах. Минимальный размер составляет 5 слов, а максимальный - 15, что составляет 20 и 60 байт соответственно. Смещение считается от начала заголовка TCP.

З арезервировано

Зарезервировано (6 бит) для будущего использования и должно устанавливаться в ноль. Из них два (5-й и 6-й) уже определены:

  • CWR (Congestion Window Reduced) - Поле «Окно перегрузки уменьшено» - флаг установлен отправителем, чтобы указать, что получен пакет с установленным флагом ECE (RFC 3168 )
  • ECE (ECN-Echo) - Поле «Эхо ECN» - указывает, что данный узел способен на ECN (явное уведомление перегрузки) и для указания отправителю о перегрузках в сети (RFC 3168 )

Ф лаги (управляющие биты)

Это поле содержит 6 битовых флагов:

  • URG - поле «Указатель важности» задействовано (англ. Urgent pointer field is significant )
  • ACK - поле «Номер подтверждения» задействовано (англ. Acknowledgement field is significant )
  • PSH - (англ. Push function ) инструктирует получателя протолкнуть данные, накопившиеся в приёмном буфере, в приложение пользователя
  • RST - оборвать соединения, сбросить буфер (очистка буфера) (англ. Reset the connection )
  • SYN - синхронизация номеров последовательности (англ. Synchronize sequence numbers )
  • FIN (англ. final , бит) - флаг, будучи установлен, указывает на завершение соединения (англ. FIN bit used for connection termination ).

Р азмер окна

Количество байт данных начиная с последнего номера подтверждения, которые может принять отправитель данного пакета. Иначе говоря, отправитель пакета располагает для приема данных буфером длинной "размер окна" байт.

К онтрольная сумма

Поле контрольной суммы - это 16-битное дополнение к сумме всех 16-битных слов заголовка (включая псевдозаголовок) и данных. Если сегмент, по которому вычисляется контрольная сумма, имеет длину не кратную 16-ти битам, то длина сегмента увеличивается до кратной 16-ти, за счет дополнения к нему справа нулевых битов заполнения. Биты заполнения (0) не передаются в сообщении и служат только для расчёта контрольной суммы. При расчёте контрольной суммы значение самого поля контрольной суммы принимается равным 0.

У казатель важности

16-битовое значение положительного смещения от порядкового номера в данном сегменте. Это поле указывает порядковый номер октета, которым заканчиваются важные (urgent) данные. Поле принимается во внимание только для пакетов с установленным флагом URG. Используется для внеполосных данных .

О пции

Могут применяться в некоторых случаях для расширения протокола. Иногда используются для тестирования. На данный момент в опции практически всегда включают 2 байта NOP данном случае 0x01) и 10 байт, задающих timestamps . Вычислить длину поля опции можно через значение поля смещения.

М еханизм действия протокола

В отличие от традиционной альтернативы - UDP, который может сразу же начать передачу пакетов, TCP устанавливает соединения, которые должны быть созданы перед передачей данных. TCP соединение можно разделить на 3 стадии:

  • Установка соединения
  • Передача данных
  • Завершение соединения

С остояния сеанса TCP

Упрощённая диаграмма состояний TCP. Более подробно в TCP EFSM diagram (на английском языке)

Состояния сеанса TCP

CLOSED

Начальное состояние узла. Фактически фиктивное

LISTEN

Сервер ожидает запросов установления соединения от клиента

SYN-SENT

Клиент отправил запрос серверу на установление соединения и ожидает ответа

SYN-RECEIVED

Сервер получил запрос на соединение, отправил ответный запрос и ожидает подтверждения

ESTABLISHED

Соединение установлено, идёт передача данных

FIN-WAIT-1

Одна из сторон (назовём её узел-1) завершает соединение, отправив сегмент с флагом FIN

CLOSE-WAIT

Другая сторона (узел-2) переходит в это состояние, отправив, в свою очередь сегмент ACK и продолжает одностороннюю передачу

FIN-WAIT-2

Узел-1 получает ACK, продолжает чтение и ждёт получения сегмента с флагом FIN

LAST-ACK

Узел-2 заканчивает передачу и отправляет сегмент с флагом FIN

TIME-WAIT

Узел-1 получил сегмент с флагом FIN, отправил сегмент с флагом ACK и ждёт 2*MSL секунд, перед окончательным закрытием соединения

CLOSING

Обе стороны инициировали закрытие соединения одновременно: после отправки сегмента с флагом FIN узел-1 также получает сегмент FIN, отправляет ACK и находится в ожидании сегмента ACK (подтверждения на свой запрос о разъединении)

У становка соединения

Процесс начала сеанса TCP (также называемый «рукопожатие» (англ. handshake )), состоит из трёх шагов.

1. Клиент, который намеревается установить соединение, посылает серверу сегмент с номером последовательности и флагом SYN.

  • Сервер получает сегмент, запоминает номер последовательности и пытается создать сокет (буферы и управляющие структуры памяти) для обслуживания нового клиента.
  • В случае успеха сервер посылает клиенту сегмент с номером последовательности и флагами SYN и ACK, и переходит в состояние SYN-RECEIVED.
  • В случае неудачи сервер посылает клиенту сегмент с флагом RST.

2. Если клиент получает сегмент с флагом SYN, то он запоминает номер последовательности и посылает сегмент с флагом ACK.

  • Если он одновременно получает и флаг ACK (что обычно и происходит), то он переходит в состояние ESTABLISHED.
  • Если клиент получает сегмент с флагом RST, то он прекращает попытки соединиться.
  • Если клиент не получает ответа в течение 10 секунд, то он повторяет процесс соединения заново.

3. Если сервер в состоянии SYN-RECEIVED получает сегмент с флагом ACK, то он переходит в состояние ESTABLISHED.

  • В противном случае после тайм-аута он закрывает сокет и переходит в состояние CLOSED.

Процесс называется «трёхэтапным согласованием» (англ. three way handshake ), так как несмотря на то что возможен процесс установления соединения с использованием четырёх сегментов (SYN в сторону сервера, ACK в сторону клиента, SYN в сторону клиента, ACK в сторону сервера), на практике для экономии времени используется три сегмента.

Пример базового 3-этапного согласования:

TCP A TCP B

1. CLOSED LISTEN

2. SYN-SENT --> --> SYN-RECEIVED

3. ESTABLISHED <-- <-- SYN-RECEIVED

4. ESTABLISHED --> --> ESTABLISHED

5. ESTABLISHED <-- <-- ESTABLISHED

В строке 2 TCP A начинает передачу сегмента SYN, говорящего об использовании номеров последовательности, начиная со 100. В строке 3 TCP B передает SYN и подтверждение для принятого SYN в адрес TCP A. Надо отметить, что поле подтверждения показывает ожидание TCP B приёма номера последовательности 101, подтверждающего SYN с номером 100.

В строке 4 TCP A отвечает пустым сегментом с подтверждением ACK для сегмента SYN от TCP B; в строке 5 TCP B передает некоторые данные. Отметим, что номер подтверждения сегмента в строке 5 (ACK=101) совпадает с номером последовательности в строке 4 (SEQ=101), поскольку ACK не занимает пространства номеров последовательности (если это сделать, придется подтверждать подтверждения - ACK для ACK). Алгоритм Нейгла и Медленный старт

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

Для того, чтобы передающая сторона не отправляла данные интенсивнее, чем их может обработать приёмник, TCP содержит средства управления потоком. Для этого используется поле «окно». В сегментах, направляемых от приёмника передающей стороне, в поле «окно» указывается текущий размер приёмного буфера. Передающая сторона сохраняет размер окна и отправляет данных не более, чем указал приёмник. Если приёмник указал нулевой размер окна, то передача данных в направлении этого узла не происходит, пока приёмник не сообщит о большем размере окна.

В некоторых случаях передающее приложение может явно затребовать протолкнуть данные до некоторой последовательности принимающему приложению, не буферизируя их. Для этого используется флаг PSH. Если в полученном сегменте обнаруживается флаг PSH, то реализация TCP отдает все буферизированные на текущий момент данные принимающему приложению. «Проталкивание» используется, например, в интерактивных приложениях. В сетевых терминалах нет смысла ожидать ввода пользователя после того, как он закончил набирать команду. Поэтому последний сегмент, содержащий команду, обязан содержать флаг PSH, чтобы приложение на принимающей стороне смогло начать её выполнение.

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

Завершение соединения можно рассмотреть в три этапа:

  1. Посылка серверу от клиента флага FIN на завершение соединения.
  2. Сервер посылает клиенту флаги ответа ACK , FIN, что соединение закрыто.
  3. После получения этих флагов клиент закрывает соединение и в подтверждение отправляет серверу ACK , что соединение закрыто.

Transmission Control Protocol (TCP) (протокол управления передачей) - один из основных сетевых протоколов Интернета, предназначенный для управления передачей данных в сетях и подсетях TCP/IP.

1) Вспомним о модели OSI


место TCP в модели OSI - это занимаемые 4 и 5-ый уровень:

TCP - это транспортный механизм, предоставляющий поток данных, с предварительной установкой соединения, за счёт этого дающий уверенность в достоверности получаемых данных, осуществляет повторный запрос данных в случае потери данных и устраняет дублирование при получении двух копий одного пакета. В отличие от UDP гарантирует целостность передаваемых данных и уведомление отправителя о результатах передачи.


Реализация TCP, как правило, встроена в ядро ОС, хотя есть и реализации TCP в контексте приложения.

Когда осуществляется передача от компьютера к компьютеру через Интернет, TCP работает на верхнем уровне между двумя конечными системами, например, браузером и веб-сервером. Также TCP осуществляет надежную передачу потока байтов от одной программы на некотором компьютере к другой программе на другом компьютере. Программы для электронной почты и обмена файлами используют TCP.
TCP контролирует =

  1. длину сообщения
  2. скорость обмена сообщениями
  3. сетевой трафик.

Структура TCP заголовка:


или можно изучить эту гламурную картинку:

флаги:

Номер последовательности

Номер последовательности выполняет две задачи:

Если установлен флаг SYN, то это начальное значение номера последовательности - ISN (Initial Sequence Number), и первый байт данных, которые будут переданы в следующем пакете, будет иметь номер последовательности, равный ISN + 1.
В противном случае, если SYN не установлен, первый байт данных, передаваемый в данном пакете, имеет этот номер последовательности.

Поскольку поток TCP в общем случае может быть длиннее, чем число различных состояний этого поля, то все операции с номером последовательности должны выполняться по модулю 2^32. Это накладывает практическое ограничение на использование TCP. Если скорость передачи коммуникационной системы такова, чтобы в течение MSL (максимального времени жизни сегмента) произошло переполнение номера последовательности, то в сети может появиться два сегмента с одинаковым номером, относящихся к разным частям потока, и приёмник получит некорректные данные.
Номер подтверждения

Если установлен флаг ACK, то это поле содержит номер последовательности, ожидаемый получателем в следующий раз. Помечает этот сегмент как подтверждение получения.
Смещение данных

Это поле определяет размер заголовка пакета TCP в 4-байтных (4-октетных) словах. Минимальный размер составляет 5 слов, а максимальный - 15, что составляет 20 и 60 байт соответственно. Смещение считается от начала заголовка TCP.
Зарезервировано

Зарезервировано (6 бит) для будущего использования и должно устанавливаться в ноль. Из них два (5-й и 6-й) уже определены:

CWR (Congestion Window Reduced) - Поле «Окно перегрузки уменьшено» - флаг установлен отправителем, чтоб указать, что получен пакет с установленным флагом ECE (RFC 3168)
ECE (ECN-Echo) - Поле «Эхо ECN» - указывает, что данный узел способен на ECN (явное уведомление перегрузки) и для указания отправителю о перегрузках в сети (RFC 3168)

Флаги (управляющие биты)

Это поле содержит 6 битовых флагов:

URG - Поле «Указатель важности» задействовано (англ. Urgent pointer field is significant)
ACK - Поле «Номер подтверждения» задействовано (англ. Acknowledgement field is significant)
PSH - (англ. Push function) инструктирует получателя протолкнуть данные, накопившиеся в приемном буфере, в приложение пользователя
RST - Оборвать соединения, сбросить буфер (очистка буфера) (англ. Reset the connection)
SYN - Синхронизация номеров последовательности (англ. Synchronize sequence numbers)
FIN (англ. final, бит) - флаг, будучи установлен, указывает на завершение соединения (англ. FIN bit used for connection termination).

В этом поле содержится число, определяющее в байтах размер данных, которые отправитель готов принять.

Механизм действия протокола

Механизм действия протокола

В отличие от традиционной альтернативы - UDP, который может сразу же начать передачу пакетов, TCP устанавливает соединения, которые должны быть созданы перед передачей данных. TCP соединение можно разделить на 3 стадии:

  1. Установка соединения
  2. Передача данных
  3. Завершение соединения

В связи с чем можно говорить о состояниях TCP сеанса:

Состояния TCP сеанса:

байт и обычно представляются 32-битными секциями (4 байта) с адресами, опциями и другими настройками сеансов.


Рис. 6.1.

Рассмотрим сначала IP -часть, так как это нижний уровень сетевой модели . Заголовок протокола IP содержит адреса получателя и отправителя пакета. Так как каждый адрес занимает 32 бита (4 октета по 8 бит каждый), то исходный и целевой IP -адреса вместе составляют 8 байт . В первой части заголовка помещаются различные переключатели и опции пакета. Первая строка содержит несколько бит , которые идентифицируют версию IP . Большинство сетей используют IP версии 4 ( IPv4 ), но более новая 128-битная система IP , называемая IP версии 6 ( IPv6 ), существует уже несколько лет и постепенно получает признание. Предполагается, что IPv6 разрешит проблемы адресного пространства IP , выделяя до 128 бит для адресной части, что должно удовлетворить любые мыслимые потребности. IPv6 решает также проблемы безопасности и верификации , имеющиеся в IPv4 . Но в настоящее время вы в подавляющем большинстве случаев будете видеть пакеты IPv4 . Затем следуют значения длины заголовка и типа сервиса (последний служит для дифференциации при определении приоритетов пакетов). Заключительный фрагмент этой строки представляет общую длину заголовка, которая обычно одинакова для всех пакетов (20 байт ), но может меняться для новых протоколов, таких как IPv6 .

В двух следующих строках располагаются идентификатор пакета и контрольная сумма , помогающая проверять его корректность . Наконец, имеются IP -адреса отправителя и получателя и поле опций, которое может иметь переменную длину или дополняться нулями и произвольными данными.

Заголовок TCP отвечает за создание сеанса TCP и функции более высокого уровня. Обычно он имеет длину 20 байт и начинается с номеров исходного и целевого портов по 16 бит каждый. Именно поэтому номера портов не могут быть больше 65535 (2 16 равно 65536). (Интересно, что выбор всех этих чисел, вроде бы абсолютно произвольный, всегда на чем-то основан.)

Номера портов , как упоминалось ранее, определяют программу, которой необходимо направлять пакеты на удаленной машине, и идентифицируют сеанс на локальной машине. Следующая строка содержит порядковый номер. Он используется для сборки пакетов в правильном порядке на удаленном конце, даже если они приходят в другом порядке. Это один из аспектов отказоустойчивости сеансов TCP . Кроме того, имеется номер подтверждения, также длиной 32 бита, который позволяет проверить, что пакет идет с правильной машины. Следующая строка содержит 4-битную секцию, называемую смещением данных, которая определяет, сколько 32-битных строк или "слов" имеется в заголовке (обычно 4); за ней располагаются 6 бит , зарезервированных для будущих применений. Затем следует 6-битная секция, называемая флагами TCP . Вторая половина этой строки служит для согласования размера окна и говорит получателю, сколько бит готов принять отправитель. Флаги TCP весьма важны, здесь задаются различные управляющие биты, контролирующие обработку пакетов. Каждый тип коммуникаций TCP задается одним битом, единица соответствует включению, ноль - отключению. В табл. 6.1 перечислены шесть полей раздела флагов TCP и описано их применение. Примечание: Каждое " поле " имеет ширину один бит (просто - единица или ноль, включено или выключено).

Таблица 6.1. Поля флагов TCP
Флаг TCP Полное имя Описание
URG Указатель срочности Показывает приоритет TCP-пакетов
ACK Подтверждение Помечает этот пакет как подтверждение получения
PSH Выталкивание Выталкивает поставленные в очередь данные из буферов
RST Сброс Сбрасывает соединение TCP по завершении или после разрыва
SYN Синхронизация Синхронизирует соединение
FIN Завершение Завершает передачу

Обычно только один или два из этих флагов установлены (биты заданы как единица ), но, как мы видели в "Сканеры портов" , ничто не мешает отправить пакет со всеми битами взведенными (сканирование XMAS) или выключенными (сканирование NULL ), чтобы попытаться запутать удаленную систему.

Следом располагается контрольная сумма TCP и указатель срочности. Затем идет строка со всеми TCP -опциями пакета, такими как дополнительные контрольные суммы, метки времени и т.д. Эта строка дополняется до 32 бит нулями, если опции не заполняют ее целиком. Наконец, следует полезная нагрузка - данные пакета. Может показаться, что на отправку одного пакета уходит слишком много накладных административных расходов (примерно 48 байт для каждого пакета), но это на самом деле обеспечивает относительно устойчивое соединение в сети, которая не всегда обладает сквозной надежностью (как Интернет ). В действительности, чтобы избежать дополнительных расходов TCP , некоторые протоколы, не требующие соединения, используют UDP - протокол без установления соединений с меньшими накладными расходами.

В стандартном сеансе Tcpdump с обычным уровнем подробности вывода вы увидите метку времени, за которой следует порядковый номер TCP . Затем выдаются IP -части, включая исходный и целевой адреса, разделенные знаком > (больше), означающим, что пакет идет отсюда туда. В конце располагается информационное поле , которое показывает, что делает пакет. Можно использовать опции -v или -vv , чтобы получить от Tcpdump более подробные данные о заголовке (см. следующий раздел).

Обычно вы будете запускать Tcpdump с некоторыми установленными опциями или фильтрами, чтобы уменьшить и сфокусировать вывод . Общий вид инструкции запуска Tcpdump таков:

Tcpdump опции выражения

Замените опции и выражения одной или несколькими допустимыми переменными. Опции Tcpdump перечислены в табл. 6.2 .

Таблица 6.2. Опции Tcpdump
Опция Описание
-a Пытается преобразовать адреса в имена. Это создает дополнительную нагрузку на систему и может привести к потере пакетов
-c число Останавливает Tcpdump после обработки заданного числа пакетов
-C размер_файла Ограничивает размер выходных файлов заданным числом байт
-d Выдает процедуру сопоставления пакетов с образцом в удобочитаемом виде и затем останавливается
-dd Выдает процедуру сопоставления пакетов с образцом в виде фрагмента программы на языке Си
- ddd Выдает процедуру сопоставления пакетов с образцом в виде десятичных чисел
-e В каждой строке выдачи печатает заголовок канального уровня (в сетях Ethernet это MAC-адрес )
-E алгоритм:секрет Использует встроенную в Tcpdump возможность расшифровывать на лету пакеты, зашифрованные по протоколу IPsec ESP . Разумеется, чтобы использовать эту опцию, вы должны располагать разделяемым секретным ключом. В число возможных значений параметра "алгоритм" входят des - cbc , 3des - cdc , blowfish - сетевых интерфейсов
-n Не преобразовывает адреса в имена
-N Не печатает в именах хостов имя домена вышележащего уровня. Это полезно, если вам необходимо представить обезличенную версию вывода и вы не хотите раскрывать, чья это сеть
-p Не переводит интерфейс в режим прослушивания. Используется только при исследовании трафика, направленного в анализирующий компьютер
-q Печатает быстрый вывод. Печатается меньше протокольной информации, поэтому строки оказываются короче
-T тип Заставляет интерпретировать пакеты, выбранные заданным в выражении фильтром, в соответствии с указанным типом
-t Не печатает метку времени в каждой строке
-tt Печатает неформатированную метку времени в каждой строке
-ttt Печатает интервал времени между пакетами
-tttt Печатает в каждой строке дату, а затем метку времени в подразумеваемом формате
-v Использует чуть более подробный вывод, включающий время жизни, идентификатор, общую длину и поля опций каждого пакета
-vv Предоставляет более детальный вывод. Пакеты NFS и SMB полностью декодируются
-vvv Предоставляет еще более подробный вывод. Это может существенно замедлить работу анализатора
-w имя_файла Записывает пакеты в указанный файл вместо вывода их на экран. Таким образом результаты "вынюхивания" без участия человека можно сохранить и проанализировать их позже. Например, если в вашей сети происходят какие-то странные вещи, вы можете запустить Tcpdump на ночь, чтобы перехватить весь необычный трафик. Не забудьте написать хороший фильтр, иначе полученный наутро файл может оказаться слишком большим
-x Выводит каждый пакет (без заголовка канального уровня) в шестнадцатеричном виде.
-X Выводит содержимое пакетов и в шестнадцатеричном, и в текстовом видах

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

Привет, меня зовут Гленн Фидлер и я приветствую вас в первой статье из моей онлайн-книги “Сетевое программирование для разрабочиков игр”.

В этой статье мы начнем с самых базовых аспектов сетевого программирования - приема и передачи данных по сети. Прием и передача данных - это основная и наиболее простая часть из всего круга задач, которыми занимаются сетевые программисты, но часто бывает сложно определить, каким путем лучше двигаться. Уделите этой части достаточно внимания - если у вас останется непонимание, то это может привести к ужасным последствиям для вашей многопользовательской игры в дальнейшем!

Вы, скорее всего, уже что-нибудь слышали о сокетах, и, возможно, знаете, что они делятся на два основных типа - TCP и UDP. Первое, что нужно решить при разработке многопользовательской игры - это какой тип сокетов использовать - TCP, UDP, или оба?

Выбор типа сокетов полностью зависит от жанра игры, которую разрабатываете. В данном цикле статей я буду считать, что вы пишете игру в стиле action - наподобие Halo, Battlefield 1942, Quake, Unreal, CounterStrike, Team Fortress и т.п.

Теперь мы более подробно рассмотрим свойства каждого типа сокетов (учитывая тот факт, что мы разрабатыватаем игру в стиле action), и немного углубимся в детали работы сети интернет. После подробного обзора правильный вариант станет очевиден!

TCP расшифровывается как “transmission control protocol” (протокол контроля передачи), а IP - как “internet protocol”. Вместе они лежат в основе практически всего, что вы делаете в сети, начиная от просмотра веб-страниц и кончая общением в IRC и электронной почтой - все это работает на основе TCP/IP.

Если вы когда-либо уже использовали TCP сокеты, то вы должны знать, что TCP - это протокол, использующий принцип надежного соединения. Это означает, что вы устанавливаете соединение между двумя компьютерами, и затем пересылаете данные между ними подобно тому, как если бы вы записывали информацию в файл на одном компьютере, а на другом - считывали бы ее из того же файла.

При этом соединение считается надежным и последовательным - то есть, вся информация, которую вы посылаете, гарантированно должна дойти до получателя в том же порядке, в каком была отправлена. Также TCP соединение можно считать непрерывным потоком данных - протокол сам заботится о разбивке данных на пакеты и пересылке их по сети.

Еще разок - все просто, как обычная запись или чтение из файла. Элементарно, Ватсон!

Но такая простота в обращении совершенно отличается от того, что на самом деле происходит «под капотом», на более низком уровне - уровне протокола IP.

На этом уровне нет понятия соединения - вместо этого отдельные пакеты передаются от одного компьютера к другому. Можно представить этот процесс как передачу записки от одного человека к другому в комнате, полной народу: в конце концов записка попадает к кому надо, но при этом пройдя через множество рук.

При этом нет никакой гарантии того, что записка дойдет до адресата. Отправитель просто отправляет записку в надежде, что она дойдет, но при этом даже не знает, дошло ли послание или нет - до тех пор, пока получатель не решит написать в ответ.
Естественно, в реальности все немного сложнее, поскольку компьютер-отправитель не знает точную последовательность компьютеров в сети, через которые надо передать пакет, чтобы он добрался как можно быстрее. Иногда IP передает несколько копий одного и того же пакета, которые могут идти до адресата разными путями - и, скорее всего, дойдут в разное время.

А что, если мы захотим пересылать информацию между компьютерами не в стиле чтения/записи в файл, а непосредственно отправляя и получая отдельные пакеты?

Что ж, мы можем сделать это, используя UDP. UDP расшифровывается как “user datagram protocol” (протокол пользовательских датаграмм), и он работает поверх IP (как и TCP), но вместо добавления кучи функциональности он представляет собой лишь небольшую надстройку над IP.

Используя UDP, мы можем отослать пакет по определенному IP адресу (к примеру, 112.140.20.10) и порту (к примеру, 52423), и он будет передаваться от компьютера к компьютеру, пока не достигнет цели (или не потеряется по пути).

При этом, на стороне приемника мы просто сидим и ждем, прослушивая определенный порт (52423 в нашем случае), и, когда на него приходит пакет от кого-либо (помним, что соединения не используются), мы получаем об этом уведомление с адресом и портом компьютера-отправителя, размером пакета, и после этого можем прочитать данные из этого пакета.

Протокол UDP не гарантирует доставку данных. На практике большинство пакетов, конечно, доходят, но всегда имеются потери около 1-5%, а иногда бывают периоды времени, в которые пакеты вообще не доходят (помните, что между отправителем и получателем могут находиться тысячи компьютеров, на любом из которых что-то может отказать или сломаться).

Также UDP не гарантирует порядок доставки пакетов. Вы можете отправить пять пакетов по порядку - 1, 2, 3, 4, 5 - а прийти они могут совершенно в другом порядке - к примеру, 3, 1, 2, 5, 4. Опять же, на практике, они скорее всего придут в правильном порядке в большинстве случаев, но полагаться на это нельзя!

Наконец, хоть UDP и ничего особо не добавляет к IP, одну вещь он все-таки гарантирует. Если вы пересылаете пакет, то он либо дойдет полностью, либо не дойдет вообще. Так, если вы пересылаете пакет в 256 байт другому компьютеру, то он не может получить только первые 100 байт от пакета - он обязательно должен получить все 256 байт. Это реально единственная вещь, которую гарантирует протокол UDP - все остальное ложится на ваши плечи.

Итак, нам нужно решить - использовать TCP или UDP сокеты? Давайте взглянем на их свойства:

  • Использует принцип соединений
  • Гарантирует доставку и очередность
  • Автоматически разбивает информацию на пакеты
  • Следит за тем, чтобы не пересылать данные слишком интенсивно (контроль потока данных)
  • Легко использовать - как запись/чтение из файла
UDP:
  • Не использует принцип соединений - придется реализовывать это вручную
  • Не гарантирует доставку и порядок доставки пакетов - они могут дойти в неправильном порядке, с дубликатами, или вообще не дойти!
  • Нужно вручную разбивать данные на пакеты и отправлять их
  • Нужно следить за тем, чтобы не пересылать данные слишком интенсивно
  • Если пакет потеряется, то нужно как-то это отследить, и в случае необходимости переслать его заново
С таким списком решение кажется очевидным - TCP реализует всю необходимую нам функциональность и его проще использовать, тогда как использование UDP обещает геморрой с написанием всего на свете вручную, с нуля. Значит, используем TCP, да?

А вот и нет.

Использовать TCP - это наверное, худшая ошибка, которую можно совершить, разрабатывая многопользовательскую игру. Чтобы понять почему, давайте разберемся, что делает TCP таким простым в использовании!

Как работает TCP
TCP и UDP оба работают поверх IP, но по факту они совершенно разные. UDP ведет себя очень похоже на IP, в то время как TCP абстрагирует пользователя от всех проблем с пакетами, делая взаимодействие с ним похожим на чтение/запись в файл.

Итак, как же он это делает?

Во-первых, TCP использует абстракцию потока данных - вы можете просто записывать байты данных в этот поток, и TCP позаботится о том, чтобы они дошли до адресата. Так как протокол IP передает данные пакетами, а TCP работает поверх IP, TCP должен разбивать поток входных данных пользователя на отдельные пакеты. Таким образом, внутри TCP некоторая логика собирает данные в очередь, и, когда их накапливается достаточно много, она формирует пакет и отправляет его адресату.

Такое поведение может стать проблемой для нашей многопользовательской игры, если нужно передавать очень маленькие пакеты. Может случиться так, что TCP решит не передавать наши данные, пока их не накопится достаточно, чтобы сформировать пакет определенного размера (скажем, больше ста байт). И это - большая проблема, потому что необходимо передавать данные с клиента (нажатия клавиш игрока) на сервер как можно быстрее, и если при этом будут возникать задержки из-за буферизации данных протоколом, то для игрока на клиентской стороне игра будет происходить далеко не самым приятным образом. При этом обновление объектов игры будет происходить с задержкой и редко - тогда как нам нужно делать обновление объектов вовремя и часто.

В TCP есть опция, призванная исправить это - “TCP_NODELAY”. Она говорит протоколу, чтобы он не ждал накопления данных в очереди на отправку, а отсылал их сразу.

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

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

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

Но что будет, если один из пакетов не дойдет? Или если пакеты придут не по порядку, или с дубликатами?

Если особо не углубляться в детали работы TCP (а это реально очень сложная тема - можете почитать в TCP/IP Illustrated), процесс выглядит так: TCP отправляет пакет, определяет, что пакет не дошел, и заново отправляет тот же пакет адресату. Дублирующиеся пакеты отсеиваются на стороне адресата, а пакеты, пришедшие не по порядку - переупорядочиваются, чтобы все было как надо - надежно и по порядку.

Проблема заключается в том, что когда TCP таким образом “синхронизирует” поток данных, в случае потери пакета передача останавливается до тех пор, пока потерянный пакет не будет отправлен заново (и получен адресатом). Если во время ожидания придут новые данные, они будут поставлены в очередь, и вы не сможете прочитать их, пока не дойдет тот самый потерянный пакет. Сколько времени занимает посылка пакета заново? Она занимает как минимум время, равное времени прохождения пакета туда и обратно (когда TCP определяет, какой пакет надо отправить заново), плюс время на повторную доставку потерянного пакета. Так что, если пинг между компьютерами составляет 125 мс, повторная передача пакета займет примерно одну пятую секунды, а в худшем случае - до полсекунды (представьте, если вдруг заново отправленный пакет тоже потеряется). Веселуха!

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

Рассмотрим простой пример многопользовательской игры, например, 3d-шутер. Сетевая часть в игре построена очень просто: каждую итерацию цикла игры клиент посылает на сервер описание всех действий игрока (нажатые клавиши, положение мыши и т.п.), и каждую итерацию сервер обрабатывает эти данные, обновляет модель игрового мира и посылает обратно клиенту текущие позиции объектов мира, чтобы тот отрисовал игроку новый кадр.

Итак, в нашей игре, если пакет будет потерян при передаче по сети, игра останавливается и ждет, пока пакет не будет доставлен заново. На клиентской стороне игровые объекты замирают, и на сервере игроки также не могут двигаться или стрелять, так как сервер не может принимать новые пакеты. Когда потерянный пакет наконец доходит, в нем содержится уже устаревшая информация, которая уже является неактуальной. К тому же после этого приходят и все те пакеты, которые накопились в очереди за время ожидания, и их всех нужно обработать за одну итерацию цикла. Полная неразбериха!

К сожалению, изменить такое поведение TCP никак нельзя, да и не надо, так как в нем и заключается смысл TCP. Это - необходимость, чтобы сделать передачу данных через интернет надежным и последовательным потоком данных.
Но нам не нужен надежный и последовательный поток данных.

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

Но подождите! Почему я не могу использовать и UDP, и TCP вместе?

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

Конечно, велико искушение использовать UDP для передачи данных пользовательского ввода и состояния мира, а TCP - для тех данных, которые должны быть гарантированно доставлены. Возможно, вы даже думаете, что можно сделать несколько “потоков” команд - например, один для загрузки уровней, другой - для команд AI. Вы думаете: “Мне не нужно, чтобы команды AI ждали в очереди, если потеряется пакет с данными для загрузки уровня, ведь они же совершенно не связаны!”. В данном случае вы правы, и вы можете решить создать по TCP сокету на каждый поток команд.

На первый взгляд, это отличная идея. Но проблема в том, что раз TCP и UDP оба работают поверх IP, пакеты обоих протоколов будут влиять друг на друга - уже на уровне IP. Как конкретно будет проявляться это влияние - очень сложный вопрос, и связан он с механизмами обеспечения надежности в TCP. Но, в любом случае, знайте, что использование TCP обычно приводит к увеличению потерь UDP пакетов. Если хотите узнать об этом больше, можете прочитать

Формат заголовка TCP

Сегменты протокола TCP состоят из заголовка и блока данных.

Рис. 1.41. Формат заголовка сегмента TCP

Заголовок сегмента имеет следующие поля:

    Порт источника (SOURCE PORT) занимает 2 байта, идентифицирует процесс-отправитель;

    Порт назначения (DESTINATION PORT) занимает 2 байта, идентифицирует процесс-получатель;

    Номер последовательности (SEQUENCE NUMBER) занимает 4 байта, указывает номер байта, который определяет смещение сегмента относительно потока отправляемых данных; TCP применяет специальный алгоритм PAWS для защиты от перехода номеров последовательности через ноль;

    Номер подтверждения (ACKNOWLEDGEMENT NUMBER) занимает 4 байта, содержит максимальный номер байта в полученном сегменте, увеличенный на единицу; именно это значение используется в качестве квитанции;

    Смещение данных (Data Offset) занимает 4 бита, указывает длину заголовка сегмента TCP, измеренную в 32-битовых словах. Длина заголовка не фиксирована и может изменяться в зависимости от значений, устанавливаемых в поле OPTIONS;

    Резерв (RESERVED) занимает 6 битов, поле зарезервировано для последующего использования;

    Кодовые биты (CODE BITS) занимают 6 битов, содержат служебную информацию о типе данного сегмента, задаваемую установкой в единицу соответствующих битов этого поля:

      URG – признак наличия срочных (внеполосных) данных.

      ACK – квитанция на принятый сегмент. Контрольный бит сегмента-подтверждения, не занимающего какого-либо места в очереди (буфере приема). Бит информирует о том, что поле подтверждения в данном сегменте определяет номер байта в очереди, который хочет получить программа протокола TCP, пославшая данный сегмент. Это означает подтверждение факта получения всех предшествующих сегментов в очереди.

Примечание:

Обычно сегмент с ACK=1 является служебным сегментом, не несущим никаких данных. В некоторых реализациях TCP существует возможность в этом сегменте послать и данные (задержанный ACK, о котором речь шла выше).

      PSH – запрос на отправку сообщения без ожидания заполнения буфера;

Примечание:

PUSH-флаг можно увидеть в почти каждых примерах TCP-дампов. Это уведомление отправителя для получающего модуля TCP-получателя, чтобы тот сразу передал все полученные данные принимающему процессу уровня приложения. Эти данные могут состоять из того, что находится непосредственно в сегменте с PUSH-флагом, наряду с любыми другими данными, которые модуль TCP до этого накопил в буфере для процесса-получателя.

В первоначальной спецификации TCP было предложено, чтобы интерфейс программирования позволял передающему процессу сообщать своему TCP-модулю, когда установить PUSH флаг. В диалоговом приложении, например, когда клиент послал команду на сервер, клиент установит PUSH флаг и будет ждать ответ сервера. Предлагая TCP-модулю установить флаг, клиентское приложение уведомляет его, что оно не хочет, чтобы данные задержались в буфере TCP и ожидали дополнительные данные, прежде, чем посылать сегмент серверу.

Точно так же, когда TCP-модуль сервера получил сегмент с PUSH-флагом, это было уведомлением, чтобы передать данные к процессу сервера и не ждать прибытия любых дополнительных данных.

Сегодня, однако, большинство API не позволяют приложению сообщить его TCP-модулю о том, что оно хочет установить флаг PUSH или сообщить, был ли PUSH-флаг установлен в полученных данных. Фактически потребность в PUSH-флаге устарела, и хорошая реализация TCP может самостоятельно решить, когда надо установить флаг. Большинство реализаций типа Berkeley автоматически устанавливают PUSH-флаг, если данные в посылаемом сегменте "опустошают" посылающий буфер. Поэтому мы обычно видим установленный PUSH-флаг для каждой операции write () приложения, потому что данные обычно посылаются после этой операции. Реализации типа Berkeley игнорируют полученный PUSH-флаг, так как они обычно никогда не задерживают поставку полученных данных к приложению.

      RST – указывает на необходимость уничтожения канала связи. Контрольный бит (бит перезагрузки), который не занимает места в очереди приема и указывает, что получатель этого бита должен ликвидировать соединение без каких-либо дополнительных действий. Получатель может, основываясь на анализе номера очереди и поля подтверждения в сегменте, принесшем данный сегмент, решить, следует ли выполнять операцию перезагрузки или же следует проигнорировать эту команду. Ни в коем случае получатель сегмента с битом RST не должен давать в ответ ту же команду RST.

Для всех состояний, кроме SYN_SENT (см. ниже), все сегменты с сигналом перезагрузки (RST) проходят проверку полей номера последовательности SEQ. Сигнал перезагрузки признается, если его номер очереди попадает в окно приема. В состоянии же SYN_SENT (сигнал RST получен в ответ на посылку инициирующего сигнала SYN), сигнал RST признается, если поле ACK подтверждает ранее сделанную посылку сигнала SYN. Получатель сигнала RST вначале проверяет его, и лишь потом меняет свое состояние. Если получатель (сервер) находился в состоянии LISTEN, то он игнорирует сигнал. Если получатель находился в состоянии SYN_RECEIVED, то он возвращается вновь в состояние LISTEN. В иных случаях получатель ликвидирует соединение и переходит в состояние CLOSED. Если получатель находится в каком-либо ином состоянии, то он ликвидирует соединение и прежде чем перейти в состояние CLOSED, оповещает об этом своего клиента. В общем случае сигнал "сброс" (reset) посылается модулем TCP в том случае, если прибывающие сегменты не принадлежат указанному соединению или когда запрос о соединении прибывает и при этом не существует процесса, который слушает порт назначения.

      SYN – признак сообщения, используемого для синхронизации счетчиков переданных данных при установлении соединения; сегменты, несущие этот бит, будем называть SYN-сегментами;

      FIN – признак достижения передающей стороной последнего байта в потоке передаваемых данных.

Примечание:

В одном сегменте может быть установлено более чем один из четырех флагов, однако обычно взведен бывает только один или два флага. В RFC-1025 сегмент, в котором максимальная комбинация всех доступных флагов «взведена» одновременно (SYN, URG, PSH, FIN и 1 байт данных), называется пакетом "Камикадзе" (в английском языке существует еще несколько определений подобного пакета, а именно - "грязный пакет", "пакет Новогодней елки" и т.п.).

    Размер окна (WINDOW) занимает 2 байта, содержит объявляемое значение размера окна приема в байтах; Начальное значение этого поля является своеобразной константой, также как IP -TTL, характеризующей данную ОС. В некоторых случаях для однозначного определения типа ОС достаточно извлечь значение поля Window в TCP-заголовке принятого сегмента. Так, ОС AIX - единственная ОС, имеющая значение Window=0x3F25. Стек TCP/IP в ОС Windows типа NT5, как и OpenBSD и FreeBSD, имеет Window=0x402E;

    Контрольная сумма (CHECKSUM) занимает 2 байта, рассчитывается по сегменту. Если сегмент содержит нечетное число байтов в заголовке/или тексте, последние байты дополняются справа 8 нулями для выравнивания по 16-битовой границе. Биты заполнения (0) не передаются в сегменте и служат только для расчета контрольной суммы. При расчете контрольной суммы значение самого поля контрольной суммы принимается равным 0;

    Срочный указатель (URGENT POINTER) занимает 2 байта, используется совместно с кодовым битом URG, указывает на один байт данных, которые необходимо срочно принять, несмотря на возможное переполнение буфера;

    Опции (OPTIONS) – это поле имеет переменную длину и может вообще отсутствовать; используется для решения вспомогательных задач, например, при объявлении максимального размера сегмента; опции разрешено слать только в SYN и ACK+SYN сегментах (более подробно об опциях TCP см. ниже.)

    Заполнитель (PADDING) может иметь переменную длину, представляет собой фиктивное поле, используемое для доведения размера заголовка до целого числа 32-битовых слов;

    После заполнителя идут непосредственно байты данных сегмента.

Следует отдельно остановиться на особой важности сегмента с установленным флагом SYN. Если на локальном компьютере (хосте) не функционирует ни один сервер на базе TCP, то получение им из сети первого сегмента с флагом SYN является свидетельством попытки просканировать ваши открытые TCP-порты и, возможно, дальнейшего проникновения в вашу систему. Очень часто пользователи устанавливают сетевые ОС и не представляют, какие сервисные программы загружают такие ОС. Такое незнание может привести к плачевным последствиям.

На компьютере в сети обязательно должен быть установлен сетевой экран – файрволл (firewall), который отслеживает приходящие (incoming) SYN-сегменты и или оповещает пользователя, или просто отбрасывает их с соответствующей фиксацией в лог-файле. Тот же файрволл фиксирует и исходящие от ваших приложений SYN-сегменты (например, от броузера) и также оповещает пользователя, что "такая-то" программа требует выхода в Интернет. Это особенно важно для случая заражения хоста каким-либо вирусом, который пытается с вашего компьютера самостоятельно выйти в сеть, чтобы установить TCP-соединение с "вражеским" хостом – например, это может быть вирус-троян.

Тот факт, что TCP-сессия всегда начинается посылкой SYN-сегмента, привел к возникновению нового типа инициирования начала сессии с помощью посылки вместо первого SYN-сегмента ACK-сегмента – сетевые экраны (файрволлы) не препятствуют прохождению через них таких сегментов – ведь это просто служебная квитанция уже начатой легальной сессии – так они в общем-то справедливо считают. И если на одну TCP-сессию приходится всего два SYN-сегмента – туда и обратно, то ACK-сегментов может быть тысячи!

Опции в заголовке TCP

Заголовок TCP в поле опций может содержать дополнительные параметры соединения. Единственные параметры, определенные в первоначальной спецификации TCP RFC-793, это – конец списка опций, отсутствие операции (nop) и опция максимального размера сегмента MSS. Форматы наиболее часто применяемых опций приведены на рис.1.42.

Рис. 1.42. Формат основных опций TCP

Каждая опция начинается с поля ее типа длиной в 1 байт, который определяет тип опции. Опции типов 0 и 1 занимают одиночный байт. Все другие параметры имеют однобайтовое указание полной длины всех опций в байтах, которое следует за байтом типа.

Задача опции NOP "нет операции" состоит в том, чтобы позволить отправителю дополнять поля опций до величины, кратной 4 байтам. Например, если мы инициируем подключение TCP в системе 4.4BSD, программа tcpdump выводит следующие параметры TCP в начальном сегменте SYN:

Опция MSS

При установлении соединения каждая сторона может объявить MSS, который она собирается передавать. Опция MSS может быть использована только в SYN-сегменте. Если одна сторона не принимает опцию MSS от другой стороны, то используется размер по умолчанию в 536 байт, так как минимальный размер буфера для сборки пакетов (при фрагментации в IPv4) регламентирован размером 576 байт.

Опция масштабирования окна

Опция масштабирования окна wscale увеличивает определение окна TCP с 16 до 32 бит. Вместо изменения TCP-заголовка для того чтобы поместить в него окно большего размера, заголовок все так же содержит 16-битное значение, а опция определяет операцию масштабирования этого 16-битного значения. После чего TCP использует "реальный" размер окна внутри себя как 32-битное значение.

1-байтовый сдвиговый счетчик находится в диапазоне от 0 (нет масштабирования) до 14. Максимальное значение равное 14 соответствует окну размером 1.073.725.440 байт (65535 * 2 14).

Эта опция может появиться только в сегменте SYN; таким образом, коэффициент масштабирования определяется в каждом направлении при установлении соединения. Чтобы включить масштабирование окна, оба конца должны активизировать опцию в своих сегментах SYN. Сторона, осуществляющая активное открытие, посылает опцию в своем SYN-сегменте, однако сторона, осуществляющая пассивное открытие, может послать опцию, только если эта опция установлена в полученном SYN-сегменте. Коэффициент масштабирования может быть различен для каждого направления.

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

Требования к хостам Host Requirements RFC требуют, чтобы TCP принимал эту опцию в любом сегменте. (Единственная заранее определенная опция, максимальный размер сегмента, может появиться только в сегментах SYN.) Также этот документ требует, чтобы модуль TCP игнорировал любые опции, которые он не понимает. Это легко осуществимо, так как все новые опции имеют поле длины.

Представьте, что мы используем опцию масштабирования окна со сдвиговым счетчиком равным S для отправки и со сдвиговым счетчиком равным R для приема. В этом случае каждые 16 бит объявленного окна, которые мы получаем от удаленного конца, сдвигаются влево на R бит, чтобы получить реальный размер объявленного окна. Каждый раз, когда мы отправляем объявление окна на удаленный конец, мы берем реальный 32-битный размер окна, сдвигаем его вправо на S бит и помещаем получившийся результат (16-битное значение) в TCP-заголовок.

Опция временной марки

Опция временной метки (timestamp) позволяет отправителю поместить значение временной метки в каждый сегмент. Получатель возвращает это значение в подтверждении, что позволяет отправителю рассчитать RTT (Round Trip Time, период обращения) при получении каждого ACK. (Мы должны сказать "каждый ACK", а не "каждый сегмент", так как многие реализации TCP обычно подтверждают несколько сегментов с помощью одного ACK.) Обычно большинство современных реализаций рассчитывают одно RTT на окно, что вполне достаточно, если окно содержит 8 сегментов. Однако в случае, если окно имеет большие размеры, требуется лучший расчет RTT.

Эта опция устанавливается при открытии соединения таким же образом, как и опция масштабирования окна. Сторона, осуществляющая активное открытие, устанавливает опцию в своем SYN-сегменте. Только если опция получена и в SYN-сегменте удаленного партнера, она может быть установлена в следующих сегментах.

Выборочное подтверждение (TCP Selective Acknowledgement - SACK, RFC-2018).

Выборочное подтверждение (SACK) - стратегия, призванная скорректировать некоторые недостатки кумулятивного подтверждения. С помощью выборочного подтверждения получатель данных может оповещать отправителя о любых сегментах, которые прибыли успешно, так что отправителю потребуется повторно передать только фактически утерянные сегменты. Эта идеология представляет собой модификацию предложений RFC-1072 и сравнительно недавно одобрена консорциумом IETF. Она позволяет подтверждать прием данных не в порядке их поступления, как это было раньше, а выборочно и уже применяется во многих транспортных протоколах: NETBLT, XTP, RDP, NADIR и VMTP.

Для выборочного подтверждения необходимо указание 2 опций:

    Sack-Permitted (SACK разрешен). Двухбайтовая опция типа 4. Может быть послана только в SYN-сегменте.

    Собственно опция SACK. Опция типа 5 переменной длины.

На рис.1.42 эти опции не показаны.

В операционной системе Windows эта опция появилась, начиная с версии Win98SE.

  1. Внеполосные данные протокола TCP

Концепция внеполосных данных существует во многих протоколах транспортного уровня. Представим, что на другом конце соединения произошли некоторые события и о них надо срочно оповестить своего партнера по соединению. Вполне возможно, что к этому времени TCP-модуль уже занес в свой буфер передачи очередную порцию «обычных» данных. Можно было бы создать новое соединение (что в принципе и делается в «быстрых» сетях), но если соединение очень медленное, то более эффективно использовать уже существующее. Протокол TCP в таком случае использует флаг URG и срочный указатель Urgent Pointer, размещаемые в заголовке отправляемого сегмента, а сами «неотложные» данные размещаются в поле данных. О таких данных говорят, что они передаются вне потока – Out Of Band – OOB. Правда, в некоторых авторитетных источниках корректность этого названия подвергается сомнению – действительно, как мы увидим в разделе 2.9.1.1, такие данные могут быть размещены как "вне потока" обычных данных, так и внутри них. На наш взгляд, термин "внеполосные данные" следует заменить на более соответствующее истине название "срочные данные", а уж потом говорить о том, как они посылаются – вне потока или в нем.

Сегмент с установленным флагом URG посылается по соединению всегда, даже если поток передачи приостановлен. А вот сами «срочные» данные могут быть или отосланы, или нет, в зависимости от многих обстоятельств (места в буфере, количества уже отосланных данных и т.д.)

Сколько же байтов можно переслать в качестве неотложных? Пересылать то можно и несколько, но «настоящим» внеполосным байтом будет лишь один. Даже если мы запишем в параметре функции посылки данных несколько байт, то неотложным будет являться самый последний из них. Читателя, интересующегося этим вопросом более глубоко, мы отсылаем к фундаментальной книге У. Стивенса . Здесь только заметим, что внеполосные данные применяются немногими современными приложениями – это FTP, telnet и rlogin.

  1. Как передается сообщение

Работу протокола TCP легче всего понять на примере передачи сообщения между двумя машинами. Пусть некоторое приложение прикладного уровня отправляет сообщение другому приложению. При этом оно передается с верхнего уровня через протоколы прикладного уровня TCP. Предполагается, что сообщение является потоком, т.е. представляет собой последовательность байтов, передаваемых асинхронно. Это контрастирует с большинством протоколов, которые работают с блоками данных фиксированной длины и может обусловить некоторые проблемы на прикладном уровне, требуя преобразования, если приложение может работать только с блоками данных постоянного размера.

Основополагающей идеей в проектировании протокола является то, что каждый байт данных, посылаемый на TCP соединение, имеет номер очереди (номер байта в последовательности). Поскольку каждый байт пронумерован, то каждый из них может быть опознан. Применяемый механизм опознания является накопительным, так что правильное опознание номера N означает, что все байты с предыдущими номерами уже получены. Этот механизм позволяет регистрировать появление дубликатов в условиях повторной передачи. Нумерация байтов в пределах сегмента осуществляется так, чтобы первый байт данных сразу за заголовком имел наименьший номер, а следующие за ним байты имели номера по возрастающей.

Важно помнить о том, что количество номеров для очереди, хоть и велико, но ограничено. Диапазон номеров – от 0 до 2**32-1.

Модуль TCP получает поток байтов и собирает его в блоки, называемые сегментами, добавляя заголовки в начало сегментов. В заголовок записывается контрольная сумма и порядковый номер сегмента данных. Длина сегмента обычно определяется TCP или выбирается администратором системы. (В большинстве случаев длины сегмента TCP и пакета IP никак не связаны друг с другом.)

Процесс установления соединения начинается с передачи запроса на установление соединения от машины-отправителя машине-получателю. В запросе содержится числовая комбинация, называемая адресом сокета (см. 2.4). В ответ приложение-получатель посылает адрес своего сокета. Набор адресов сокетов отправителя и получателя однозначно определяют соединение между приложениями.

После установления соединения модуль TCP начинает передавать сегменты сообщения IP-модулю, который преобразует каждый из них в один IPv4-пакет. Эти операции производятся уже без какого-либо участия TCP. Пройдя через сеть от машины-отправителя к машине-получателю, пакеты поступают к IP-уровню последней. Он собирает из них отправленный сегмент и передает его своему модулю TCP. В свою очередь сообщение от TCP поступает к приложению-получателю через используемый протокол прикладного уровня.

Если сообщение состоит из нескольких ТСР-сегментов (не путать с IР-пакетами), TCP-модуль на машине-получателе собирает его, исходя из порядковых номеров сегментов, хранящихся в заголовке. Если сегмент утерян или поврежден (последнее обнаруживается с помощью контрольной суммы в заголовке сегмента), отправителю посылается сообщение, содержащее порядковый номер ошибочного или потерянного сегмента. В этом случае отправитель повторно передает сегмент.

  1. Таймеры TCP

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

Таймер повторной передачи

В TCP не предусмотрена передача квитанции, если сегмент доставлен искаженным. Пропажу сегмента обнаруживают с помощью таймера повторной передачи, и если квитанция на сегмент не поступила вовремя, он считается потерянным и передается повторно. Программное обеспечение TCP отправителя хранит в специальном буфере копии сегментов, на которые не получено подтверждение. После получения квитанции таймер останавливается, а копия соответствующего сегмента удаляется.

Таймер повторной передачи отмеряет время ожидания квитанции на отправленный сегмент (Retransmission TimeOut, RTO). Этот параметр устанавливается с учетом типа сети и скоростей доставки сообщений. Если квитанция не поступает вовремя, сегмент отправляется вновь, а период повторной передачи увеличивается по экспоненциальному закону. Так повторяется несколько раз, пока период повторной передачи не достигнет некоторого заданного предела, после чего обслужи-ваемому процессу выдает сообщение об ошибке.

Выбор времени ожидания (тайм-аута) очередной квитанции является важной задачей, результат решения которой влияет на производительность протокола TCP.

Тайм-аут не должен быть слишком коротким, чтобы по возможности исключить избыточные повторные передачи, которые снижают полезную пропускную способность системы. Но он не должен быть и слишком большим, чтобы избежать длительных простоев, связанных с ожиданием несуществующей или "заблудившейся" квитанции.

При выборе величины тайм-аута должны учитываться скорость и надежность физических линий связи, их протяженность и многие другие подобные факторы. В протоколе TCP тайм-аут определяется с помощью достаточно сложного адаптивного алгоритма. При установлении соединения засекается время от момента отправки сегмента до прихода квитанции о его приеме (так называемое время оборота – RTT, Round Trip Time), на основании которого формируется начальное значение RTO. При дальнейших передачах получаемые значения RTT усредняются с весовыми коэффициентами, возрастающими от предыдущего замера к последующему. Это делается для усиления влияния последних замеров. В качестве тайм-аута выбирается среднее время оборота, умноженное на некоторый коэффициент. Практика показывает, что значение этого коэффициента должно превышать 2. В сетях с большим разбросом времени оборота при выборе тайм-аута учитывается и дисперсия этой величины.

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

" Только первый " . Поддерживается один таймер повторной передачи для всей очереди. При получении подтверждения первый сегмент удаляется из очереди повторной передачи и таймер сбрасывается. Если время таймера истекает, повторно передается сегмент из начала очереди и таймер обнуляется.

Пакетная . Также поддерживается один таймер повторной передачи для всей очереди, но когда приходит подтверждение, из очереди повторной передачи удаляются все сегменты и таймер сбрасывается. Если время таймера истекает, повторно передаются все сегменты из очереди и таймер обнуляется.

Индивидуальная . Для каждого сегмента в очереди существует отдельный таймер. При получении подтверждения из очереди повторной передачи удаляется первый сегмент, а соответствующий таймер обнуляется. По истечении времени какого-либо таймера повторно передается только соответствующий сегмент и его таймер сбрасывается.

Первая стратегия повышает эффективность передачи трафика, поскольку повторно передается лишь потерянный сегмент (или тот, чье подтверждение о приеме было потеряно). Однако из-за того, что таймер для второго сегмента в очереди не устанавливается, пока не подтвержден прием первого сегмента, могут возникать некоторые задержки. Использование пакетного режима также снижает вероятность длительных задержек, но способно привести к ненужным повторным передачам. Индивидуальная стратегия решает эти проблемы ценой более сложной реализации.

Таймер задержки

Получателю могут поступать сегменты и после того, как соединение было им закрыто. Таймер задержки (quiet timer) связан с состоянием ожидания TIME_WAIT (см. раздел 1.7.13), которое предоставляет защиту от опоздавших пакетов, принадлежащих ранним соединениям; при этом они не будут интерпретироваться как часть нового соединения, которое использует те же самые локальный и удаленный IP адреса и номера портов. Это состояние иногда называется состоянием 2MSL – удвоенное время жизни TCP-сегмента Maximum Segment Lifetime.

Следуя RFC-793, в котором MSL определен равным 2 мин., общее время TIME_WAIT составит 4 мин. На практике это обычно не так. Например, в системах, производных от BSD, MSL равно 30 сек., так что состояние TIME_WAIT длится всего 1 мин. Можно встретить и другие значения в диапазоне от 30 сек. до 2 мин. Если в то время, когда процесс находится в состоянии TIME_WAIT, прибывает новый сегмент, то его таймер 2MSL перезапускается.

Возможно, что хост или его процесс, находясь в состоянии 2MSL, вышел из строя, за время 2MSL успел перезапуститься и немедленно установил новые соединения с использованием локальных и удаленных адресов сокетов, которые были в распоряжении процесса в состоянии 2MSL перед выходом из строя.

В этом случае опоздавшие сегменты из соединения, которое существовало перед выходом из строя сетевой программы хоста, ОС или самого хоста, могут быть ошибочно интерпретированы как принадлежащие новому соединению, созданному после перезапуска. Это может произойти вне зависимости от того, какой начальный номер последовательности сгенерировал модуль TCP после перезагрузки.

Чтобы защититься от подобных нежелательных ситуаций, RFC-793 указывает, что модуль TCP не должен создавать новые соединения до истечения MSL с момента перезагрузки. Этот период называется "тихое время" (quiet time).

В некоторых реализациях хосты после перезагрузки ожидают даже дольше, чем время MSL.

Длительность задержки обычно выбирают равной удвоенному значению максимального времени жизни сегмента (оно совпадает со значением поля времени жизни в заголовке IP-пакета). В ответ на каждый пришедший в этот период сегмент отправляется сообщение об ошибке.

Таймер запросов

Таймер запросов (persistence timer) предусмотрен для такого довольно редкого случая, когда получатель, приостановивший передачу данных путем посылки сегмента с нулевым размером окна, отправляет своему партнеру сообщение о возобновлении работы, но тот его не получает. Если подтверждения теряются, то это в принципе может привести к тупиковой ситуации, когда обе стороны будут дожидаться прихода подтверждений. В таком случае, чтобы продолжить передачу, отправитель с периодом, задаваемым таймером, посылает запросы с одним байтом данных. В ответ на них он получает сегменты, где указан размер окна партнера. Если размер окна нулевой, адресат по-прежнему занят, а если нет, то он готов принимать полезную информацию, после чего передача данных возобновляется. Период повторения запросов определяются таймером запросов. Обычно это 60 секунд. Он взводится в момент получения TCP-пакета с нулевым значением поля "Размер окна" в его заголовке (типичное начальное значение для этого таймера - 5 секунд.)

Таймер контроля и таймер разъединения

Эти таймеры предназначены для проверки соединения. Будучи однажды создан, канал TCP может существовать "вечно". Если клиент и сервер пассивны, то при разрыве соединения, например, при проблемах со средой передачи, сетевой атаке, крахе сервера или клиента, участники соединения (либо один из них) не подозревают о возникших проблемах. Конечно, проблема рано или поздно будет выявлена - когда клиент или сервер попытаются послать какую-то информацию.

В архитектуре клиент-сервер довольно часто встречаются реализации, в которых клиент, отправив запрос на сервер, долгое время ожидает ответа сервера. Еще более актуальная ситуация - в реализациях TCP-сервера необходимо точно знать, сколько из соединившихся клиентов реально существуют. Многие из прикладных протоколов применяют для этого "пустую операцию" (NOP), которая время от времени производится между клиентом и сервером для проверки наличия соединения. Данный подход хорош тем, что он не зависит от реализации стека TCP/IP. Есть и другой метод – таймер контроля работоспособности (keep-alive).

Transporta un Sakaru Instit ūts , dabas zinātņu maģ ... - 2002 Baltijas Krievu instit ūts , lektore, docente. Transporta un sakaru instit ūts , lektore. SIA «L ...

  • Profesionālā bakalaura grāda un datordizainera kvalifikācijas iegūšanai

    Документ

    Iestāde Amats No 2005 Transporta un sakaru instit ūts docente No 2004 Baltijas Starptautisk ... 2000 – 2005 Transporta un sakaru instit ūts lektore 2000 – 2002 Baltijas Krievu instit ūts lektore 2000 – 2002 ...

  • Rīgas tehniskā universitāte datorzinātnes un informācijas tehnoloģijas fakultāte datorvadības automātikas un datortehnikas institūts pašnovērtējuma ziņojums

    Документ

    Pakalpojumu laboratorijā - viss notiek!. Sakaru pasaule. #2(46) 2007.g. 70 ... uzdevumi matemātikas papildnodaļās transporta un mašīnzinību spacialitātēm. RTU ... 1993. g. Profesors, Elektronikas un datorzinātnes instit ūts , Latvijas Universitāte (Profesora ...

  • Latvijas Izglītības un zinātnes ministrijai (3)

    Документ

    ... un psiholoģijas katedra/ instit ūts , docente 1999. - 2002.g. Pedagoģijas un psiholoģijas instit ūts ... ājums, 37. – 39. lpp. Rīga, Transporta un sakaru instit ūts , 2005 J.Mencis, V.Neimanis. GENESIS OF ...