Связь по tcp. Структура пакета TCP (формат заголовка сегмента). Разбираемся с HTTP

Связь по tcp. Структура пакета TCP (формат заголовка сегмента). Разбираемся с HTTP

04.04.2019

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

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

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

Вы, скорее всего, уже что-нибудь слышали о сокетах, и, возможно, знаете, что они делятся на два основных типа - 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 пакетов. Если хотите узнать об этом больше, можете прочитать

Применяя программу - анализатор трафика и используемых протоколов - Wireshark , Вы можете наблюдать работу трехэтапного квитирования TCP:


Шаг 1

Клиент TCP начинает трехэтапное квитирование, отправляя сегмент с установленным контрольным флагом SYN (Синхронизировать Номер Последовательности), указывая первоначальное значение в поле номера последовательности в заголовке. Это первоначальное значение номера последовательности, известное как Начальный Номер Последовательности (ISN), выбирается случайным образом и используется, чтобы начать отслеживание потока данных от клиента на сервер для этой сессии. ISN в заголовке каждого сегмента увеличивается на единицу для каждого байта данных, отправленных от клиента серверу, пока продолжается обмен данными.

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

Контрольный Флаг SYN установлен, и относительный номер последовательности равен 0. Хотя анализатор протоколов на графике указывает относительные значения для номеров последовательности и подтверждения, истинные значения является двоичными 32-битными числами. Мы можем определить фактические номера, отправляемые в заголовках сегментов, исследуя область "Packet Bytes" (Байты Пакета). Здесь можно видеть четыре байта, представленные в шестнадцатеричной форме.

Шаг 2

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

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

В протоколе TCP-соединения устанавливаются с помощью «тройного рукопожатия», описанного в разделе «Установка соединения». Чтобы установить соединение, одна сторона (например, сервер) пассивно ожидает входящего соединения, выполняя примитивы LISTEN и ACCEPT, либо указывая конкретный источник, либо не указывая его.

Другая сторона (например, клиент) выполняет примитив CONNECT, указывая IP-адрес и порт, с которым он хочет установить соединение, максимальный размер TCP-сегмента и, по желанию, некоторые данные пользователя (например, пароль). Примитив CONNECT посылает TCP-сегмент с установленным битом SYN и сброшенным битом АСК и ждет ответа.

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

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

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

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

Разрыв соединения TCP

Хотя TCP-соединения являются полнодуплексными, чтобы понять, как происходит их разъединение, лучше считать их парами симплексных соединений. Каждое симплексное соединение разрывается независимо от своего напарника. Чтобы разорвать соединение, любая из сторон может послать TCP-сегмент с установленным в единицу битом FIN, что означает, что у него больше нет данных для передачи. Когда этот TCP-сегмент получает подтверждение, это направление передачи закрывается. Тем не менее, данные могут продолжать передаваться неопределенно долго в противоположном направлении. Соединение разрывается, когда оба направления закрываются. Обычно для разрыва соединения требуются четыре TCP-сегмента: по одному с битом FIN и по одному с битом АСК в каждом направлении. Первый бит АСК и второй бит FIN могут также содержаться в одном ТСР-сегменте, что уменьшит количество сегментов до трех.

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

Чтобы избежать проблемы двух армий, используются таймеры. Если ответ на посланный FIN-сегмент не приходит в течение двух максимальных интервалов времени жизни пакета, отправитель разрывает соединение. Другая сторона в конце концов заметит, что ей никто не отвечает, и также разорвет соединение. Хотя такое решение и не идеально, но, учитывая недостижимость идеала, приходится пользоваться тем, что есть. На практике проблемы возникают довольно редко.

Управление передачей в TCP

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

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

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

Отправители не обязаны передавать данные сразу, как только они приходят от приложения. Также никто не требует от получателей посылать подтвержде­ния как можно скорее. Например TCP-сущность, получив от прило­жения первые 2 Кбайт данных и зная, что доступный размер окна равен 4 Кбайт, была бы совершенно права, если бы просто сохранила полученные данные в буфере до тех пор, пока не прибудут еще 2 Кбайт данных, чтобы передать сразу сегмент с 4 Кбайт полезной нагрузки. Эта свобода действий может использоваться для улучшения производительности.

Рассмотрим TELNET-соединение с интерактивным редактором, реагирующим на каждое нажатие клавиши. В худшем случае, когда символ прибывает к передающей TCP-сущности, она создает 21-байтовый TCP-сегмент и передает его IP-уровню, который, в свою очередь, посылает 41-байтовую IP-дейтаграмму.

На принимающей стороне TCP-сущность немедленно отвечает 40-байтовым подтверждением (20 байт TCP-заголовка и 20 байт IP-заголовка). Затем, когда редактор прочитает этот байт из буфера, TCP-сущность пошлет обновленную информацию о размере буфера, передвинув окно на 1 байт вправо. Размер этого пакета также составляет 40 байт. Наконец, когда редактор обработает этот символ, он отправляет обратно эхо, передаваемое 41-байтовым пакетом. Итого для каждого введенного с клавиатуры символа пересылается четыре пакета общим размером 162 байта. В условиях дефицита пропускной способности линий этот метод работы нежелателен.

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

Хотя такой метод задержки и снижает нагрузку на сеть, тем не менее, эффективность использования сети отправителем продолжает оставаться невысокой, так как каждый байт пересылается в отдельном 41-байтовом пакете. Метод, позволяющий повысить эффективность, известен как алгоритм Нагля (Nagle, 1984). Предложение Нагля звучит довольно просто: если данные поступают отправителю по одному байту, отправитель просто передает первый байт, а остальные помещает в буфер, пока не будет получено подтверждение приема первого байта. После этого можно переслать все накопленные в буфере символы в виде одного TCP-сегмента и снова начать буферизацию до получения подтверждения отосланных символов. Если пользователь вводит символы быстро, а сеть медленная, то в каждом сегменте будет передаваться значительное количество символов, таким образом, нагрузка на сеть будет существенно снижена. Кроме того, этот алгоритм позволяет посылать новый пакет, даже если число символов в буфере превышает половину размера окна или максимальный размер сегмента.

Алгоритм Нагля широко применяется различными реализациями протокола TCP, однако иногда бывают ситуации, в которых его лучше отключить. В частности, при работе приложения X-Windows в Интернете информация о перемещениях мыши пересылается на удаленный компьютер. (X-Window - это система управления окнами в большинстве ОС типа UNIX). Если буферизировать эти данные для пакетной пересылки, курсор будет перемещаться рывками с большими паузами, в результате чего пользоваться программой будет очень сложно, почти невозможно.

Еще одна проблема, способная значительно снизить производительность протокола TCP, известна под именем синдрома глупого окна (Clark, 1982). Суть проблемы состоит в том, что данные пересылаются TCP-сущностью крупными блоками, но принимающая сторона интерактивного приложения считывает их посимвольно.

Рассмотрим на примере - начальное состояние таково: TCP-буфер приемной стороны полон, и отправителю это известно (то есть размер его окна равен 0). Затем интерактивное приложение читает один символ из TCP-потока. Принимающая TCP-сущность радостно сообщает отправителю, что размер окна увеличился, и что он теперь может послать 1 байт. Отправитель повинуется и посылает 1 байт. Буфер снова оказывается полон, о чем получатель и извещает, посылая подтверждение для 1-байтового сегмента с нулевым размером окна. И так может продолжаться вечно.

Дэвид Кларк (David Clark) предложил запретить принимающей стороне отправлять информацию об однобайтовом размере окна. Вместо этого получатель должен подождать, пока в буфере не накопится значительное количество сво­бодного места. В частности, получатель не должен отправлять сведения о новом размере окна до тех пор, пока он не сможет принять сегмент максимального размера, который он объявлял при установке соединения, или его буфер не освободился хотя бы наполовину.

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

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

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

Еще одна проблема получателя состоит в сегментах, полученных в неправильном порядке. Они могут храниться или отвергаться по усмотрению получателя. Разумеется, подтверждение может быть выслано, только если все данные вплоть до подтверждаемого байта получены. Если до получателя доходят сегменты О, 1, 2, 4, 5, 6 и 7, он может подтвердить получение данных вплоть до последнего байта сегмента 2. Когда у отправителя истечет время ожидания, он передаст сегмент 3 еще раз. Если к моменту прибытия сегмента 3 получатель сохранит в буфере сегменты с 4-го по 7-й, он сможет подтвердить получение всех байтов, вплоть до последнего байта сегмента 7.

- 1

Хотя есть и реализации TCP в контексте приложения.

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

Заголовок сегмента TCP

Заголовок сегмента TCP
Бит 0 - 3 4 - 9 10 - 15 16 - 31
0 Порт источника Порт назначения
32 Номер последовательности
64 Номер подтверждения
96 Смещение данных Зарезервировано Флаги Размер Окна
128 Контрольная сумма Указатель важности
160 Опции (необязательное, но используется практически всегда)
160/192+
Данные

Порт источника

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

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

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

Окно

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

Псевдозаголовок

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

TCP-псевдозаголовок IPv4

TCP-псевдозаголовок IPv6

  • Протокол (Protocol)/Протокол верхнего уровня (Next header) - содержит в себе значение 6 (000000110 в двоичном виде, 0x6 - в шестнадцатеричном) - идентификатор TCP-протокола.
  • Длина TCP-сегмента (TCP length) - содержит в себе длину TCP-сегмента в байтах (TCP-заголовок + данные; длина псевдозаголовка не учитывается).

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

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

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

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

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

Опции

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

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

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

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

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

Состояния сеанса 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), состоит из 3 шагов.

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

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

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

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

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

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

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

Пример базового 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 совпадает с номером в строке 4, поскольку ACK не занимает пространства номеров последовательности (если это сделать, придется подтверждать подтверждения - ACK для ACK!).

Передача данных

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

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

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

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

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

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

Известные проблемы

Максимальный размер сегмента

TCP требует явного указания максимального размера сегмента (MSS) в случае, если виртуальное соединение осуществляется через сегмент сети, где максимальный размер блока (MTU) менее, чем стандартный MTU Ethernet (1500 байт).

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

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

Для решения этой проблемы на маршрутизаторах применяются правила Firewall-а, добавляющие параметр MSS во все пакеты, инициирующие соединения, чтобы отправитель использовал сегменты допустимого размера.

MSS может также управляться параметрами операционной системы.

Обнаружение ошибок при передаче данных

Хотя протокол осуществляет проверку контрольной суммы по каждому сегменту, используемый алгоритм считается слабым . Так в 2008 году не обнаруженная сетевыми средствами ошибка в передаче одного бита, привела к остановке серверов системы Amazon Web Services .

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

Атаки на протокол

Основная статья: Атаки на TCP

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

Реализация

Освобождение от расчёта контрольной суммы

Многие реализации стека TCP/IP предоставляют возможности использования аппаратной поддержки для автоматического расчёта контрольной суммы в сетевом адаптере до передачи в сеть или после приёма из сети для верификации. Это может освобождать операционную систему от использования ценных тактов процессора при вычислении контрольной суммы.

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

См. также

Ссылки

  • RFC 793 - Transmission Control Protocol

Литература

  • Терри Оглтри. Модернизация и ремонт сетей = Upgrading and Repairing Networks. - 4-е изд. - М .: «Вильямс», 2005. - С. 1328. - ISBN 0-7897-2817-6
  • Дуглас Камер. Сети TCP/IP, том 1. Принципы, протоколы и структура = Internetworking with TCP/IP, Vol. 1: Principles, Protocols and Architecture. - М .: «Вильямс», 2003. - С. 880. - ISBN 0-13-018380-6
  • Андрей Робачевский, Сергей Немнюгин, Ольга Стесик. Операционная система UNIX. - 2-е изд. - "БХВ-Петербург", 2007. - С. 656. -

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 , что соединение закрыто.


© 2024 beasthackerz.ru - Браузеры. Аудио. Жесткий диск. Программы. Локальная сеть. Windows