Транспортный уровень

Материал из Вики ИТ мехмата ЮФУ
Перейти к: навигация, поиск

Транспортный уровень

Функции транспортного уровня

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

Сокеты

Сокет (socket, гнездо) — это структура данных, идентифицирующая сетевое соединение.

Зачем нужны сокеты? Сервер (программа) может одновременно поддерживать несколько TCP-соединений с другими компьютерами, используя один и тот же стандартный номер порта. Как это реализовать? Можно возложить эту задачу на программиста. Пусть он выбирает из буфера приема сетевого уровня пакеты, смотрит от кого они отправлены и отвечает соответствующим образом. Но можно сделать все это удобнее.

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

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

Сокеты были разработаны в университете Калифорнии в городе Berkeley, стали стандартом де-факто в противоположность OSI TLI (Transport Layer Interface).

Примитивы сокетов

SOCKET создать новый (пустой) сокет
BIND сервер связывает свой локальный адрес (порт) с сокетом
LISTEN сервер выделяет память под очередь подсоединений клиентов (TCP)
ACCEPT сервер ожидает подсоединения клиента или принимает первое подключение из очереди (TCP). Чтобы заблокировать ожидание входящих соединений, сервер выполняет примитив ACCEPT. Получив запрос соединения, транспортный модуль ОС создает новый сокет с теми же свойствами, что и у исходного сокета, и возвращает описатель файла для него. После этого сервер может разветвить процесс или поток, чтобы обработать соединение для нового сокета и параллельно ожидать следующего соединения для оригинального сокета
CONNECT клиент запрашивает соединение (TCP)
SEND/SEND_TO послать данные (TCP/UDP)
RECEIVE/RECEIVE_FROM получить данные (TCP/UDP)
DISCONNECT запросить разъединение (TCP)

Мультиплексирование и демультиплексирование

Мультиплексирование — сбор сообщений от сокетов всех приложений и добавление заголовков.

Демультиплексирование — распределение приходящих данных по сокетам.

Для UDP нужный сокет определяется номером порта получателя, для TCP — номером порта получателя, IP-адресом и номером порта отправителя.

Протоколы транспортного уровня

На транспортном уровне действует два протокола: TCP (надежный) и UDP (ненадежный).

Протокол UDP

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

Структура UDP-сегмента
+ Биты 0-15 16-31
0 Порт отправителя Порт получателя
32 Длина пакета Контрольная сумма
64
Данные

Заголовок всего 8 байт.

Принципы надежной передачи данных

Спроектируем протокол myTCP, последовательно его усложняя.


Но квитанции тоже могут теряться. Если квитанция искажена отправитель опять посылает пакет. Получатель должен думать как обрабатывать повторные пакеты (нужно ввести новое состояние — передали прошлый пакет приложению или нет).

Роль идентификаторов «повторный» и «новый» в TCP/IP играют номера пакетов (т.к. пакеты еще могут теряться).

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

Теперь пришло время вспомнить, что пакеты могут теряться.

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

Таким образом, мы приходим к необходимости таймера. В случае, если прошло какое-либо определенное время и подтверждение не пришло, то осуществляется повторная отправка сообщения. Интервал времени — маленький т.к. вероятность потери принимается близкой к 1 (это и вправду так даже для хорошего WiFi-соединения).

Недостатки протоколов с ожиданием подтверждений

Рассмотрим пример. Пусть имеется 1Гб-канал Ростов — Москва. Посчитаем время отправки 1000 байт (или 8000 бит):

8000 бит / 1 Гб/с = 8 мкс

Время распространения сигнала:

1000 км / 300 000 км/с = 3333 мкс

Итого: следующие 1000 байт будут отправлены более чем через 6674 мкс

Вывод: 99,9% времени канал не использует.

Путь решения — увеличить размер пакета. Но ведь если хотя бы 1 бит исказится, то весь пакет выкинут. Что тогда?

Протоколы скользящего окна

Решение проблемы: разрешить отправителю посылать не один кадр, а несколько прежде чем остановиться и перейти в режим ожидания подтверждений (квитанций). Такая техника называется конвейерной обработкой.

Протокол скользящего окна.GIF

На рисунке зеленым обозначены те квитанции, которые уже получены, желтым — отправленные, но не полученные, голубые подготовлены к отправке, а белые нельзя отправлять, пока не получим квитанции на желтые. Окно: желтые и голубые — это пакеты, которые могут быть переданы без ожидания квитанций. Первый белый пакет может быть отправлен только после того, как получили подтверждение на первый желтый. Тогда окно подвигается на 1 вправо.

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

Есть два способа решить проблемы возникновения ошибок при конвейеризации кадров:

  • GBN (Go Back N — возвращение на N пакетов назад);
  • SR (Selective Repeat — выборочное повторение).
GBN

Получатель отправляет только положительные квитанции и только о получении тех пакетов, для которых выполняется условие: все пакеты с меньшими номерами уже получены. Таким образом, здесь используется групповое квитирование: получение отправителем квитанции с номером i означает, что все пакеты до i включительно доставлены успешно. Если по истечении некоторого времени отправитель не получает квитанции, он повторяет отправление всех N пакетов начиная со следующего за последним квитированным.

Метод GBN неэффективен при большом окне и долгом распространении пакетов по сети, в которой случаются потери. Пример: отправили 1000 пакетов, второй не пришел, приходится повторять отправку всех, начиная со второго. Мы «засоряем» сеть бесполезным трафиком.

SR

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

Часто выборочный метод комбинируют с отправкой получателем «отрицательного подтверждения» (NAK — Negative Acknowledgement) при обнаружении ошибки (например, при неверной контрольной сумме). При этом эффективность работы повышается.

При большом окне подход SR может потребовать значительного размера буфера.

Формат TCP-сегмента

Протокол TCP

Формат TCP-сегмента

TCP-сегмент состоит из поля данных и нескольких полей заголовка. Поле данных содержит фрагмент данных, передаваемых между процессами. Размер поля данных ограничивается величиной MSS (максимальный размер сегмента). Когда протокол осуществляет передачу большого файла, он, как правило, разбивает данные на фрагменты размером MSS (кроме последнего фрагмента, который обычно имеет меньший размер). Интерактивные приложения, напротив, часто обмениваются данными, объем которых значительно меньше MSS. Например, приложения удаленного доступа к сети, подобные Telnet, могут передать транспортному уровню 1 байт данных. Поскольку обычно длина заголовка ТСР-сегмента составляет 20 байт (что на 12 байт больше, чем в UDP), полный размер сегмента в этом случае равен 21 байт.

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

  • 32-разрядные поля порядкового номера и номера подтверждения. Необходимы для надежной передачи данных.
  • 4-разрядное поле длины заголовка определяет длину TCP-заголовка в 32-разрядных словах. Минимальный размер составляет 5 слов, а максимальный — 15, что составляет 20 и 60 байт соответственно. TCP-заголовок может иметь переменную длину благодаря полю параметров, описанному ниже (как правило, поле параметров является пустым; это означает, что длина заголовка составляет 20 байт).
  • Поле флагов состоит из 6 бит. Бит подтверждения (АСК) указывает на то, что значение, содержащееся в квитанции, является корректным. Биты RST, SYN и FIN используются для установки и завершения соединения. Установленный бит PSH инструктирует получателя протолкнуть данные, накопившиеся в приемном буфере, в приложение пользователя. Бит URG показывает, что в сегменте находятся данные, помещенные верхним уровнем как «срочные». Расположение последнего байта срочных данных указано в 16-разрядном поле указателя срочных данных. На принимающей стороне протокол TCP должен уведомить верхний уровень о наличии срочных данных в сегменте и передать ему указатель на конец этих данных. На практике флаги PSH, URG и поле указателя срочных данных не используются. Мы упомянули о них лишь для полноты описания.
  • 16-разрядное окно приема используется для управления потоком данных. Оно содержит количество байтов, которое способна принять принимающая сторона.
  • Указатель важности — 16-битовое значение положительного смещения от порядкового номера в данном сегменте. Это поле указывает порядковый номер октета которым заканчиваются важные (urgent) данные. Поле принимается во внимание только для пакетов с установленным флагом URG.
  • Необязательное поле параметров используется в случаях, когда передающая и принимающая стороны «договариваются» о максимальном размере сегмента, либо для масштабирования окна в высокоскоростных сетях. Также в этом поле определяется параметр временных меток. Дополнительную информацию можно найти в документах RFC 854 и RFC 1323.
Порядковые номера и номера подтверждения
Порядковые номера и номера подтверждения.JPG

Порядковый номер сегмента — это номер первого байта этого сегмента.

Номер подтверждения — это порядковый номер следующего ожидаемого байта.

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

Протокол TCP рассматривает данные как неструктурированный упорядоченный поток байтов. Такой подход проявляется в том, что TCP назначает порядковые номера не сегментам, а каждому передаваемому байту. Исходя из этого, порядковый номер сегмента определяется как порядковый номер первого байта этого сегмента. Рассмотрим следующий пример. Пусть хост А желает переслать поток данных хосту В через TCP-соединение. Протокол TCP на передающей стороне неявно нумерует каждый байт потока. Пусть размер передаваемого файла составляет 500 000 байт, величина MSS равна 1000 байт, и первый байт потока имеет порядковый номер 0. TCP разбивает поток данных на 500 сегментов. Первому сегменту присваивается порядковый номер 0, второму сегменту — номер 1000, третьему сегменту — номер 2000, и т. д. Порядковые номера заносятся в поля порядковых номеров каждого TCP-сегмента.

Теперь рассмотрим номера подтверждения. Вспомним о том, что протокол TCP обеспечивает дуплексную передачу данных, то есть через единственное TCP-соединение данные между хостами А и В могут передаваться одновременно в обе стороны. Каждый сегмент, исходящий из хоста В, содержит порядковый номер данных, передающихся от хоста В к хосту А. Номер подтверждения, который хост А помещает в свой сегмент, — это порядковый номер следующего байта, ожидаемого хостом А от хоста В. Рассмотрим следующий пример. Предположим, что хост А получил все байты с номерами от 0 до 535, посланные хостом В, и формирует сегмент для передачи хосту В. Хост А ожидает, что следующие байты, посылаемые хостом В, будут иметь номера, начинающиеся с 536, и помещает число 536 в поле номера подтверждения своего сегмента.

Рассмотрим другую ситуацию. Пусть хост А получил от хоста В два сегмента, в первом из которых содержатся байты с номерами от 0 до 535, а во втором — байты с номерами от 900 до 1000. Это означает, что по какой-либо причине байты с номерами от 536 до 899 не были получены хостом А. В этом случае хост А ожидает появления отсутствующих байтов и в поле номера подтверждения своего сегмента помещает число 536. Поскольку TCP квитирует принятые данные до первого отсутствующего байта, говорят, что он поддерживает общее квитирование.

Последний пример демонстрирует очень важный аспект функционирования протокола TCP. Третий сегмент (содержащий байты 900-1000) принят хостом А раньше, чем второй (содержащий байты 536-899), то есть с нарушением порядка следования данных. Возникает вопрос: как протокол TCP реагирует на нарушение порядка? Если полученный сегмент содержит номер последовательности больший, чем ожидаемый, то данные из сегмента буферизируется, но номер подтвержденной последовательности не изменяется. Если в последствии будет принят сегмент, относящийся к ожидаемому номеру последовательности, то порядок данных будет автоматически восстановлен исходя из номеров последовательностей в сегментах. Таким образом TCP относится к протоколам SR, но у него используется общее квитирование как в GBN. Хотя SR – не совсем чистое. Если посылаемая сторона получает несколько (3) отрицательных квитанций на один и тот же сегмент x, то она догадывается, что произошла перегрузка сети и сегменты x+1, x+2, x+3,… тоже не были доставлены. Тогда посылается вся серия начиная с x – как в протоколах GBN.

Проблемы с максимальным размером сегмента

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

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

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

Чтобы установить соединение, хост 2 пассивно ожидает входящего соединения, выполняя примитив ACCEPT.

Хост 2 выполняет примитив CONNECT, указывая IP-адрес и порт, с которым он хочет установить соединение, максимальный размер TCP-сегмента и т.п.. Примитив CONNECT посылает TCP-сегмент «Connection request» с установленным битом SYN=1 и сброшенным битом АСК=0 и ждет ответа. Таким образом, хост 1 сообщает порядковый номер x последовательности битов от хоста 1 к 2.

Хост 2 отсылает в ответ подтверждение «Connection accepted» (функция accept). Последовательность TCP-сегментов, посылаемых в нормальном случае, показана на рис: SYN=1 ASK=1, хост 2 сообщает порядковый номер x последовательности битов от хоста 2 к 1 и сообщает, что он ожидает продолжения данных начиная с байта № x+1.

Хост 1 (Connect) отправляет подтверждение о получении согласия на установление соединения.

Борьба с перегрузкой в TCP

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

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

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

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

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

Борьба с перегрузкой в TCP

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

Решение, применяемое в Интернете, состоит в признании существования двух потенциальных проблем: низкой пропускной способности сети и низкой емкости получателя — и в раздельном решении обеих проблем. Для этого у каждого отправителя есть два окна: окно, предоставленное получателем, и окно перегрузки. Размер каждого из них соответствует количеству байтов, которое отправитель имеет право передать. Отправитель руководствуется минимальным из этих двух значений. Например, получатель говорит: «Посылайте 8 Кбайт», но отправитель знает, что если он пошлет более 4 Кбайт, то в сети образуется затор, поэтому он посылает все же 4 Кбайт. Если же отправитель знает, что сеть способна пропустить и большее количество данных, например 32 Кбайт, он передаст столько, сколько просит получатель (то есть 8 Кбайт).

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

Этот процесс экспоненциального роста продолжается до тех пор, пока не будет достигнут размер окна получателя или не будет выработан признак тайм-аута, сигнализирующий о перегрузке в сети. Например, если пакеты размером 1024, 2048 и 4096 байт дошли до получателя успешно, а в ответ на передачу пакета размером 8192 байта подтверждение не пришло в установленный срок, окно перегрузки устанавливается равным 4096 байтам. Пока размер окна перегрузки остается равным 4096 байтам, более длинные пакеты не посылаются, независимо от размера окна, предоставляемого получателем. Этот алгоритм называется затяжным пуском, или медленным пуском. Однако он не такой уж и медленный (Jacobson, 1988). Он экспоненциальный. Все реализации протокола TCP обязаны его поддерживать.

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

Механизмы надежной передачи. Обобщение

Контрольная сумма Обнаружение искажений битов в принятом пакете
Таймер Отсчет интервала ожидания и указание на его истечение. Последнее означает, что с высокой степенью вероятности пакет или его квитанция потеряны при передаче. В случае, если пакет доставляется с задержкой, но не теряется (преждевременное истечение интервала ожидания), либо происходит потеря квитанции, повторная передача приводит кдублированию пакета на принимающей стороне
Порядковые номера Последовательная нумерация пакетов, посылаемых передающей стороной. «Пробелы» в номерах получаемых пакетов позволяют сделать заключение о потерях данных. Одинаковые порядковые номера пакетов означают, что пакеты дублируют друг друга
«+» и «-» квитанции Генерируется принимающей стороной и указывает передающей стороне на то, что соответствующий пакет или группа пакетов были или не были приняты. Обычно подтверждение содержит порядковые номера успешно принятых пакетов. В зависимости от протокола различают индивидуальные и групповые подтверждения
Окно/конвейер Ограничивают диапазон порядковых номеров, которые могут использоваться для передачи пакетов. Групповая передача и квитирование позволяют значительно увеличить пропускную способность протоколов по сравнению с режимом ожидания подтверждений. Размер окна может быть рассчитан на основе возможностей приема и буферизации принимающей стороны, а также уровня загрузки сети

Особенности программирования

  1. Потоковое соединение TCP
    • ситуация а) возможна при плохой связи, если промежуток времени между приходами групп дейтаграмм сетевого уровня велик:
      • компьютер1 один раз использует функцию send;
      • компьютер2 не получает всю информацию за один вызов recv (нужно несколько вызовов).
    • ситуация б) возможна, если интервал времени между вызовами функции send мал и размер данных мал:
      • компьютер1 использует функцию send несколько раз;
      • компьютер2 получает всю информацию за один вызов recv.
  2. По протоколу UDP
    • ситуация а) - невозможна
      • компьютер1 один раз использует функцию send, на сетевом уровне UDP-сегмент разбивается на несколько пакетов;
      • компьютер2 получает сегмент всегда одним вызовом recv и только, если пришли все IP-дейтаграммы.
    • ситуация б) - невозможна
      • разные вызовы функции sendto на компьютере1 соответствуют разным UDP-датаграммам и разным вызовам recvfrom на компьютере2.
  3. Если буфер в функциях recv и recvfrom меньше, чем размер присланных данных, то в случае UDP часть данных теряется, а в случае TCP – остаток сохраняется для последующего вызова recv.
  4. У UDP-сервера 1 сокет, а TCP-сервер имеет много разных сокетов (по количеству одновременно подключенных клиентов) и каждому передается своя информация.