6. Транспортный уровень Транспортный протокол - это центральный протокол во всей иерархии протоколов. Именно он обеспечивает надежную передачу данных в сети от одного абонента к другому. Здесь мы подробно рассмотрим организацию, сервис, протоколы и производительность на транспортном уровне. 6.1. Сервис В этом разделе мы рассмотрим, какие виды сервиса транспортный уровень предоставляет прикладному, как характеризуется качество предоставляемого сервиса, как обеспечивается доступ из прикладного уровня к транспортному, как выглядит интерфейс транспортного уровня со смежными уровнями. 6.1.1. Сервис для верхних уровней Основная цель транспортного уровня - обеспечить эффективный, надежный и дешевый сервис для пользователей на прикладном уровне. Достижение этой цели - задача сервиса, предоставляемого сетевым уровнем. То, что выполняет работу транспортного уровня, называется транспортным агентом. Транспортный агент располагается на абонентской машине и может располагаться там в ядре операционной системы, в отдельном процессе пользователя, в библиотеке сетевого приложения или на карте сетевого интерфейса. В некоторых случаях оператор сети может предоставлять надежный транспортный сервис, при котором транспортный агент располагается на специальной интерфейсной машине на границе транспортной среды, к которой подключены абонентские машины. На рисунке 6-1 показано взаимное расположение сетевого, транспортного и прикладного уровней. Рисунок 6-1. Сетевой, транспортный и прикладной уровень Подобно сетевому уровню, транспортный уровень также может поддерживать два вида сервиса - ориентированный на соединения и без соединений. Транспортный сервис, ориентированный на соединение, имеет много общего с аналогичным сетевым сервисом. Адресация и управление потоком также схожи на обоих уровнях. Возникает вопрос: если сервис сетевого уровня столь схож с сервисом транспортного, то зачем выделять два разных уровня? Причина состоит в том, что сетевой уровень - это часть транспортной среды, которой управляет оператор. Что будет, если сетевой уровень предоставляет ненадежный сервис, ориентированный на соединения? Предположим, что он часто теряет пакеты. Что делать, если маршрутизатор время от времени отказывает? У пользователя такой транспортной среды отсутствуют средства для решения этих проблем, в случае их возникновения. Нужно поверх сетевого пустить еще один уровень, который позволит исправлять качество сервиса сетевого уровня. Если транспортному уровню придет сообщение, что соединение на сетевом уровне неожиданно было разорвано, то он может установить новое сетевое соединение, с помощью которого выяснить, что произошло, какие данные были переданы, а какие нет, и т.п. Задача транспортного уровня в том, чтобы сделать сервис транспортного уровня для прикладного более надежным, чем сетевого для транспортного. Другое важное свойство транспортного уровня - прикладная программа, опираясь на транспортный сервис, становится независимой от сети и может работать в сети с любым сетевым сервисом. И, наконец, с транспортным сервисом работает прикладная программа, а с сетевым – транспортный уровень. Поэтому интерфейс с транспортным уровнем должен быть дружественным, удобным и эффективным. В силу приведенных доводов первые четыре уровня называют поставщиками транспортного сервиса, а все, что выше четвертого, - пользователями транспортного сервиса. 6.1.2. Качество сервиса Мы уже встречались с понятием качества сервиса при рассмотрении сетевого уровня, где рассматривали набор параметров, характеризующих это понятие. Транспортный уровень позволяет пользователю в момент установки соединения определить желаемые, допустимые и минимальные значения для различных параметров, характеризующих качество сервиса. Далее транспортный уровень сам будет решать, сможет ли он с помощью сетевого сервиса удовлетворить запросы пользователя, и до какой степени. В таблице 6-2 перечислены основные параметры качества сервиса. Заметим, что лишь немногие сети поддерживают все эти параметры. Таблица 6-2. Основные параметры качества сервиса на транспортном уровне Connection establishment delay (Задержка установления соединения) Connection establishment failure probability (Вероятность сбоя при установлении соединения) Throughput (Пропускная способность) Transit delay (Задержка при передаче) Residual error ratio (Коэффициент необнаруженных ошибок) Protection (Защита) Priority (Приоритет) Resilience (Устойчивость системы) Connection establishment delay - задержка на установку соединения, определяет время между запросом на установку соединения и подтверждением его установки. Connection establishment failure probability - вероятность, что соединение не будет установлено за время, равное задержке на установку соединения. Throughput - пропускная способность транспортного соединения, определяет количество байт пользователя, передаваемых за секунду. Transit delay - задержка на передачу, определяет время от момента, когда сообщение ушло с машины-отправителя, до момента, когда оно получено машиной-получателем. Residual error ration - доля ошибок при передаче. Этот параметр определяет отношение числа сообщений, при передаче которых были ошибки, включая потерянные сообщения, к общему числу переданных сообщений. Теоретически этот параметр должен быть равен 0, если транспортный уровень надежно передает сообщение. На практике это не так. Protection - этот параметр позволяет определить уровень защиты передаваемых данных от несанкционированного доступа третьей стороной. Косвенно он определяет, на какие затраты готов пойти пользователь для защиты своих данных от перехвата при передаче на транспортном уровне. Priority – приоритет, позволяет пользователю указать степень важности для него данного соединения среди остальных соединений. Resilience – устойчивость, определяет вероятность разрыва транспортным уровнем соединения в силу своих внутренних проблем или перегрузки. Параметры качества сервиса определяет пользователь в момент установления транспортного соединения, указывая для каждого из них желаемое и минимальное значения. Если требуемое качество недостижимо, то транспортный уровень сразу сообщает об этом пользователю, даже не обращаясь к получателю сообщения. При этом пользователя информируют, что попытка установить соединение прошла неудачно, и о причинах неудачи. Процедура согласования параметров качества сервиса называется согласованием возможностей. 6.1.3. Примитивы транспортного уровня Примитивы транспортного уровня позволяют пользователю получить доступ к транспортному сервису. Транспортный сервис похож на сервис сетевого уровня. Однако между ними существует принципиальные различия. Если задача сетевого обеспечить маршрутизацию пакетов, то задача транспортного сервиса как раз обеспечить надежную доставку сообщений. Два процесса, соединенные между собой, ничего не должны знать о том, как физически они соединены. Один помещает данные на вход транспортного уровня, другой получает их. Задача транспортного уровня скрыть и от получателя и от отправителя все детали передачи, исправления ошибок и т.п. Теоретически транспортный сервис может быть как ориентированным на соединения, так и нет. Однако дейтаграммный транспортный сервис - это редкость, поэтому мы будем рассматривать транспортный сервис, ориентированный на соединения. Другое важное различие между сетевым и транспортным сервисами состоит в том, кто их использует. Сетевой сервис использует транспортный сервис, а вот транспортный использует пользователь, т.е. прикладные программы. Поэтому транспортный сервис должен быть ориентирован на пользователя, должен быть удобным и простым в использовании. Общее представление о примитивах транспортного сервиса дает таблица 6-3. Этот пример содержит основные виды примитивов для установления соединения, передачи данных и разрыва соединения, что вполне достаточно для многих приложений. Таблица 6-3. Примитивы транспортного сервиса Примитив Отправляемый пакет TPDU Значение LISTEN (нет) Блокировка, пока какой-либо процесс не попытается установить соединение CONNECT CONNECTION REQ. Активная попытка установить соединение SEND SEND Информация об отправке RECEIVE (нет) Блокировка до поступления DATA TPDU DISCONNECT DISCONNECTION REQ. Данная сторона собирается прервать соединение Использование этих примитивов может быть продемонстрировано следующим образом. Сервер приложения выполняет примитив LISTEN, в результате чего он блокируется до поступления запросов от клиентов. Клиент для установления соединения выполняет примитив CONNECT. Транспортный агент на стороне клиента блокирует клиента и посылает серверу пакет с запросом на установление соединения. Напомним, что транспортные агенты обмениваются пакетами, которые имеют специальное название - Transport Protocol Data Unit (TPDU). Взаимосвязь между кадрами, пакетами и TPDU показана на рисунке 6-4. Рисунок 6-4. Взаимосвязь между кадрами, пакетами и TPDU Итак, по примитиву CONNECT транспортный агент на стороне клиента шлет CONNECTION REQUEST TPDU. Транспортный агент сервера, видя, что сервер заблокирован по LISTEN, разблокирует сервер и посылает CONNECTION ACCEPTED TPDU. После этого транспортное соединение считается установленным и начинается обмен данными с помощью примитивов SENT и RECEIVE. По окончании обмена соединение должно быть разорвано. Есть два варианта разрыва соединения: симметричный и асимметричный. Асимметричный разрыв предполагает, что для разрыва соединения одна из сторон посылает DISCONNECT TPDU. Получив этот TPDU, другая сторона считает соединение разорванным. При симметричном разрыве каждое направление закрывается отдельно. Когда одна сторона посылает DISCONNECT TPDU, это значит, что с ее стороны больше данных не будет. На рисунке 6-5 показана диаграмма состояний при установлении и разрыве соединения. (Переходы, выделенные курсивом, вызываются прибытием пакета. Пунктирной линией обозначена последовательность состояний сервера, сплошной - клиента.) Рисунок 6-5. Диаграмма состояний при установлении и разрыве соединения В таблице 6-6 показан другой набор примитивов, так называемые сокеты Беркли. В этом наборе есть два основных отличия от того, что мы только что рассмотрели. Они состоят в следующем. Первые четыре примитива выполняются сервером в том порядке, как они указаны в таблице. Примитив SOCKET создает новую точку подключения к серверу, резервирует для нее место в таблице транспортного агента. Параметры обращения определяют формат адреса, тип желаемого сервиса, протокол и т.д. По примитиву BIND сервер выделяет сокету адрес. Причина, по которой адрес выделяется не сразу, в том, что некоторые процессы сами управляют своими адресами, которые жестко закреплены за ними. Второе – LISTEN - не блокирующий примитив. Он выделяет ресурсы и создает очередь, если несколько клиентов будут обращаться за соединением в одно и то же время. Примитив ACCEPT - блокирующий в ожидании запроса на соединение. Таблица 6-6. Сокеты Беркли Примитив Значение SOCKET Создание новой точки подключения BIND Прикрепление локального адреса к сокету LISTEN Объявление готовности принимать соединения; сообщение о размере очереди ACCEPT Блокировка вызывающего в ожидании запроса на соединение CONNECT Активная попытка установить соединение SEND Отправка данных через данное соединение RECEIVE Получение данных через данное соединение CLOSE Прерывание соединения Когда клиент выполняет примитив CONNECT, он блокируется своим транспортным агентом, и запускается процесс установления соединения. Когда он закончится, клиента разблокируют, и начинается обмен данными с помощью примитивов SEND и RECEIVE. Разрыв соединения здесь симметричен, т.е. соединение считается разорванным, если обе стороны выполнили примитив CLOSE. 6.6.2. Элементы транспортного протокола Транспортный сервис реализует транспортный протокол, который используют транспортные агенты. Транспортный протокол в чем-то схож с канальным. Однако между ними несколько различий: 1.Они работают в разных средах (см. рисунок 6-7). 2.Процессы на канальном уровне взаимодействуют непосредственно через физическую среду, поэтому процедура установления соединения много проще. 3.Среда, в которой работает транспортный протокол, использует память, которая может терять свое содержимое. 4.Количество соединений, которое может возникать на транспортном уровне, намного больше, чем количество соединений на канальном уровне, что создает дополнительные проблемы для буферизации и управления потоком. Рисунок 6-7. (а) Среда канального уровня; (b) Среда транспортного уровня Транспортный протокол должен решать следующие проблемы: 1.Как адресовать прикладной процесс, с которым надо установить соединение? 2.Как корректно установить соединение? Пакеты могут теряться. Как отличить пакеты нового соединения от повторных пакетов, оставшихся от старого? 3.Как корректно разрывать соединение? Далее мы последовательно рассмотрим существующие решения для этих проблем. 6.2.1. Адресация Проблема адресации состоит в том, как указать, с каким удаленным прикладным процессом надо установить соединение. Обычно для этого используется транспортный адрес, по которому прикладной процесс может слушать запросы на соединение. Вместо него мы будем здесь использовать термин TSAP - Transport Service Access Point. Аналогичное понятие существует и на сетевом уровне, это IP-адрес - NSAP для сетевого уровня. На рисунке 6-8 показана взаимосвязь ТSAP и NSAP. Он же иллюстрирует сценарий использования ТSAP для установления соединения между двумя удаленными процессами. Рисунок 6-8. Взаимодействие TSAP и NSAP Из этой иллюстрации не ясно лишь, как прикладной процесс на машине 1 узнает, что интересующий его сервер подключен к ТSAP 122 на машине 2? Одно из возможных решений - если данный сервер всегда подключен к ТSAP 122, и все процессы об этом знают. Такое решение хорошо работает для часто используемого сервиса с длительным периодом активности, но как быть прикладным процессам пользователя, которые активизируются спорадически на короткое время? Одно из решений, используемых в операционной системе Unix, показано на рисунке 6-9. Оно называется протоколом установления начального соединения. На каждой машине есть специальный сервер процессов, который представляет все процессы, исполняемые на этой машине. Этот сервер слушает несколько ТSAP, куда могут поступить запросы на ТСР-соединение. Если нет свободного сервера, способного выполнить запрос, то соединение устанавливается с сервером процессов, который переключит соединение на нужный сервер, как только он освободится. Рисунок 6-9. Пользовательский процесс на хосте 1 устанавливает соединение с сервером времени Однако есть случаи, когда этот подход с сервером процессов не работает. Не всегда можно запускать сервер сервиса по требованию пользователя. Например, файловый сервер. Он должен существовать всегда. Решение в этом случае - сервер имен. Пользователь устанавливает соединение с сервером имен, для которого ТSAP известен, и передает ему имя 6.2.2. Установление соединения Проблема установления транспортного соединения сложна потому, что пакеты могут теряться, храниться и дублироваться на сетевом уровне. Типичный пример - установление соединения с банком для перевода денег с одного счета на другой. Из-за перегрузки в сети или по какой-либо другой причине может произойти большая задержка. Тогда по time_out активная сторона вышлет еще один запрос. Пакеты-дубли могут вызвать повторное соединение и вторичный перевод денег. Как быть? Одно из возможных решений - временное ТSAP. После того, как оно использовано, TSAP с таким адресом более не возникает. При этом решении не работает модель с сервером процессов (рисунок 6-9), когда определенные TSAP имеют фиксированные адреса. Другое решение - каждому транспортному соединению сопоставлять уникальный номер. Когда соединение разрывается, этот номер заносится в специальный список. К сожалению, этот список может расти бесконечно. Кроме этого, в случае сбоя машины он может быть потерян, и тогда... Альтернативой может быть ограничение времени жизни пакетов. Достичь этого можно тремя путями: 1.ограничением конструкции подсети 2.установкой счетчиков скачков в каждом пакете 3.установкой временной метки на каждом пакете Заметим, что последний метод требует синхронизации маршрутизаторов в сети. На практике нам надо обеспечить, чтобы стали недействительными не только сами пакеты, но и уведомления о них. Это значит, что надо ввести величину Т – интервал времени, который в несколько раз может превышать время жизни пакета. Эта величина Т получается умножением максимального времени жизни пакета в сети на некоторый коэффициент. Конкретное значение этого коэффициента зависит от конкретного протокола и позволяет немного увеличить реальное время жизни так, чтобы по истечении периода времен длины  Т в сети не осталось ни самого пакета, ни уведомления о нем. При ограничении времени жизни пакета можно построить безопасный способ установления соединения. Этот метод был предложен Томлинсоном (Tomlinson). Его идея состоит в следующем. Все машины в сети оснащены таймерами. Таймер работает даже в случае сбоя машины, т.е. он абсолютно надежен. Каждый таймер - двоичный счетчик достаточно большой разрядности, равной или превосходящей разрядность последовательных чисел, используемых для нумерации пакетов. При установлении соединения значения нескольких младших разрядов этого таймера берутся в качестве начального номера последовательности пакетов. Главное, чтобы последовательности номеров пакетов одного соединения не приводили к переполнению счетчика и его обнулению. Количество разрядов для нумерации TPDU должно быть достаточно большим, чтобы к моменту исчерапания счетчика все пакеты, возникшие вначале, уже исчезли из сети. Проблема возникает, когда машина восстанавливается после сбоя. Транспортный агент не знает в этот момент, какое число можно использовать для очередного номера. Чтобы избежать повторного использования порядкового номера, который уже был сгенерирован перед сбоем машины, вводится специальная величина по времени, которая образует область запрещенных номеров (см. рисунок 6-10). Рисунок 6-10. (а) TPDU не могут попасть в запрещенную область; (b) Проблема ресинхронизации Необходимость введения этой области демонстрирует следующий пример. Предположим для простоты, что в момент x начальный номер будет x, и что максимальное время жизни пакета равно 60 сек. Пусть в t=30 сек. был послан пакет с номером 80, после чего наступил сбой в работе. Пусть в момент t=60 машина была восстановлена после сбоя, и в момент t=70 появился пакет с номером 80. Однако старый пакет с номером 80 все еще существует, так как он будет жить до t=90. Поэтому для каждого момента времени вводят область запрещенных номеров. Машина, восстановленная после сбоя, не может выбирать номера из этой запретной зоны. Поэтому после восстановления следует подождать Т сек., пока все ранее посланные пакеты не перестанут существовать. На практике поступают иначе, чтобы не тратить впустую эти Т сек. Строят кривую скорости генерации номеров n=at, тогда после сбоя надо выбирать номера по формуле: n=at+T. Проблема номеров может возникать по двум причинам. Либо потому, что машина генерирует слишком быстро пакеты и соединения, либо потому, что делает это слишком медленно. Чем больше разрядность счетчика последовательных номеров, тем дальше отодвигается момент попадания в запретную область. Другая нетривиальная проблема - надежное установление соединения: пакеты ведь могут пропадать. Для ее решения Томлинсон предложил процедуру «троекратного рукопожатия» (three-way handshake), которая проиллюстрирована на рисунке 6-11. (CR и ACK обозначают соответственно CONNECTION REQUEST и CONNECTION ACCEPTED.) Рисунок 6-11. Установление соединения с помощью «троекратного рукопожатия»: (а) Нормальная ситуация; (б) Старый дубль CONNECTION REQUEST возникает «ниоткуда»; (с) Дубли CONNECTION REQUEST и ACK Эта процедура предполагает, что машина 1 шлет запрос на установление соединения под номером x. Машина 2 шлет подтверждение на запрос x, но со своим номером у. Машина 1 подтверждает получение подтверждения с номером у. На рисунке 6-11 (b) и (c) показано, что будет, если поступит запоздавший запрос на соединение и запрос и подтверждение на него. 6.2.3. Разрыв соединения Разрыв соединения, как уже было сказано, может быть асимметричным или симметричным. Асимметричный разрыв может привести к потере данных (см. рисунок 6-12). Рисунок 6-12. Разрыв соединения с потерей данных Симметричный разрыв каждая сторона проводит самостоятельно, когда она передала весь имеющийся объем данных. Однако определить этот факт не всегда просто. Здесь есть одна проблема, которая называется проблемой двух армий (см. рисунок 6-13). Суть этой проблемы в следующем. Пусть есть две противоборствующие армии, скажем, А и В. Армия А представлена двумя группировками, между которыми расположена армия В. Суммарно ресурсы А превосходят ресурсы В, и, если обе группировки А ударят по В, то А победит. Дело лишь за тем как договорится, чтобы обе группировки ударили одновременно. Имеется сложность – гонец от А должен пройти через территорию, контролируемую В. Пусть группировка №1 шлет гонца с донесением, в котором указано время атаки. Вопрос, выслав гонца, может ли армия №1 выступать? Конечно, нет! Если гонец не доставил донесение, то атака будет отбита, ресурсы потрачены, и В победит. Выход из создавшегося положения – дождаться гонца от армии №2 с подтверждением. Пусть гонец от армии №2 прибыл. Можно ли наступать? Опять нельзя! Не получив подтверждения, что гонец доставил подтверждение, армия №2 не может выступить. Этот процесс ожидания подтверждений можно продолжать сколь угодно долго. Рисунок 6-13. Проблема двух армий Внимательно изучив проблему разрыва соединения, мы придем к выводу, что ни одна армия не начнет атаки до тех пор, пока не получит подтверждения на подтверждение, и так до бесконечности. На самом деле, можно доказать, что нет протокола, который безопасно разрешает эту ситуацию. На рисунке 6-14 показаны четыре сценария разрыва соединения: нормальный случай с «тройным рукопожатием», с потерей последнего подтверждения, с потерей ответа и с потерей ответа и последующих данных. Обычно эту проблему решают, фиксируя число попыток разрыва. На практике поступаю так: если по соединению была сделана попытка разрыва и не поступало никаких TPDU-пакетов в течении определенного времени, то соединение считается разорванным. Рисунок 6-14. Четыре сценария разрыва соединения: (а) Нормальный случай трехкратного рукопожатия; (b) Потеря последнего ACK; (c) Потеря ответа; (d) Потеря ответа и последующих DR сервиса. В ответ сервер имен шлет надлежащий ТSAP. Клиент разрывает соединение с сервером имен и устанавливает его по полученному адресу. Пусть пользователь узнал ТSAP, но как он узнает, на какой машине этот ТSAP расположен, какой сетевой адрес надо использовать? Ответ заключается в структуре ТSAP-адреса, где указана вся необходимая информация. Этот адрес имеет иерархическую структуру. 6.2.4. Управление потоком и буферизация Теперь, рассмотрев, как устанавливают соединение, обратимся к тому, как им управляют. Прежде всего, рассмотрим управление потоком. Проблема управления потоком на транспортном уровне в чем-то аналогична проблеме управления потоком на канальном уровне. Различия в том, что у маршрутизатора число каналов невелико, в то время как на транспортном уровне соединений может быть очень много. Канальный протокол сохранял пакеты как на стороне отправителя, так и на стороне получателя до тех пор, пока они не будут подтверждены. Если у нас есть 64 соединения и поле «время жизни» пакета занимает 4 разряда, то нам потребуется суммарная емкость буферов на 1024 TPDU-пакетов. Число буферов можно сократить, если есть информация о надежности сетевого уровня или о наличии буфера у получателя. На транспортном уровне отправитель сохраняет все пакеты на случай, если какой-то из них придется посылать вторично. Если получатель знает об этом, то он может иметь лишь один пул буферов для всех соединений, и, если пришел пакет и ему нет буфера в пуле, то он сбрасывается, в противном случае сохраняется и подтверждается. Если сетевой уровень не надежный, то на транспортном уровне отправитель вынужден сохранять все отправленные пакеты до тех пор, пока они не будут подтверждены. При надежном сетевом сервисе, наоборот, отправителю нет нужды сохранять отправленные пакеты, если он уверен, что у получателя всегда есть буфер для сохранения полученного TPDU. Если такой уверенности нет, то ему придется сохранять пакеты. Однако и в первом и во втором случае возникает проблема размера буфера. При фиксированной длине буфера естественно организовывать пул буферов одного размера. Однако при переменной длине пакетов проблема становится много сложнее. Если размер буфера устанавливать по максимальной длине пакета, то мы столкнемся с проблемой фрагментации, т.е. неэффективного использования пространства. Если по минимальной длине, то один пакет придется пересылать как несколько, с дополнительными накладными расходами. Можно установить схему динамического согласования размера буфера при установлении соединения. Оптимальное соотношение между буферизацией на стороне отправителя или на стороне получателя зависит от типа трафика. Для низкоскоростного, нерегулярного трафика буферизацию лучше делать на обоих концах. В общем случае вопрос о количестве буферов лучше всего решать динамически. Здесь надо только позаботиться о решении проблемы потери управляющих пакетов. При этом надо учитывать, что динамическое выделение буферов усложнит алгоритм скользящего окна. На рисунке 6-15 и в таблице 6-16 показано управление окном в случае динамической буферизации в дейтаграммной сети с 4-битовыми порядковыми номерами. Другую проблему представляет согласование доступного числа буферов и пропускная способность сетевого уровня. Дело в том, что пропускная способность транспортной среды между двумя определенными хостами ограниченна. Это ограничение вытекает из ограниченности числа путей между транспортными агентами в сети и ограниченностью пропускной способности каждого маршрута. Если поток между ними превысит пропускную способность транспортной среды, то возникнет перегрузка. Эту проблему лучше всего решать динамически с помощью управляющих сообщений. Механизм управления потоком должен прежде всего учитывать пропускную способность подсети, а уже потом - возможности буферизации. Располагаться этот механизм будет на стороне отправителя, чтобы предотвращать накопление большого числа неподтвержденных сообщений. В 1975 году Белснесом был предложен алгоритм скользящего окна, который следит за соответствием размера окна и пропускной способности подсети, и, при необходимости, настраивает этот размер. Пропускная способность подсети за определенный интревал времени может быть оценена по-разному. Например, как число подтвержденных TPDU за такой интервал. Естественно, что отправитель в период измерения пропускной спосбности подсети таким способом должен посылать TPDU-пакеты с максимальной скоростью. Рисунок 6-15. (а) Цепочка из буферов фиксированного размера; (b) Цепочка из буферов разного размера; (с) Один круговой буфер на соединение Таблица 6-16. Динамическое выделение буфера. Стрелки показывают направление передачи. Многоточие означает потерю TPDU A Сообщение B Комментарии 1 А требует 6 буферов. 2 В разрешает только сообщения 0-3. 3 У А осталось только 3 буфера. 4 У А осталось только 2 буфера. 5 ... Сообщение потеряно, но А полагает, что у него осталось еще 1. 6 В подтверждает 0 и 1, разрешает 2-4. 7 У А есть свободный буфер. 8 У А не осталось буферов, необходима остановка. 9 У А истекает тайм-аут и он совершает повторную передачу. 10 Все подтверждено, но А все еще заблокирован. 11 А теперь может оправлять 5. 12 В обнаруживает новый буфер. 13 У А есть 1 свободный буфер. 14 А снова заблокирован. 15 А все еще заблокирован. 16 ... Потенциальная взаимоблокировка. 6.2.5. Мультиплексирование Потребность в мультиплексировании нескольких потоков одного уровня на одном соединении, виртуальном канале, физической линии на других уровнях возникает постоянно. Эта проблема возникает и на транспортном уровне. Например, если пользователь за терминалом установил транспортное соединение и отошел попить кофе, то транспортное соединение продолжает поддерживаться, под него резервируется буферное пространство, пространство в таблице маршрутизации и т.д. В целях удешевления стоимости транспортных соединений можно отобразить несколько транспортных соединений на одно сетевое. Такое отображение называется восходящим мультиплексированием. Оно показано схематично на рисунке 6-17 (а). Рисунок 6-17. (а) Восходящее мультиплексирование; (b) Нисходящее мультиплексирование В некоторых случаях, наоборот, в целях увеличения пропускной способности по отдельным транспортным соединениям можно отобразить транспортное соединение на несколько сетевых и по каждому сетевому иметь свое скользящее окно. Тогда, быстро исчерпав возможности одного оконного буфера, можно переключиться на другое сетевое соединение и продолжить передачу по нему. В этом случае мы получим канал, пропускная способность которого равна сумме пропускных способностей отдельных каналов на сетевом уровне. Такое мультиплексирование называется нисходящим (рисунок 6-17 (b)). 6.2.6. Восстановление после сбоев Восстановление после сбоев мы будем рассматривать в предположении, что транспортный агент целиком располагается на абонентской машине. Восстановление сетевого уровня достаточно просто. Если сетевой уровень предоставляет дейтаграммный сервис, то транспортный уровень знает, как исправлять подобные ситуации. При сервисе, ориентированном на соединение, транспортный уровень восстановит потерянное соединение и постарается в диалоге с транспортным агентом на другой стороне выяснить, что успели передать, а что нет. Проблема становится сложнее, когда надо восстанавливать работоспособность машины, включая и транспортный уровень. Рассмотрим случай, когда транспортный сервер взаимодействует с клиентами. Предположим, сервер упал и старается восстановить функционирование. Прежде всего, ему надо узнать у клиента, какое TPDU было последним неподтвержденным, и попросить повторить его. В свою очередь, клиент может находиться в одном из двух состояний: S1 – есть неподтвержденное TPDU, либо S0 – все TPDU подтверждены. Казалось бы, все просто. Однако рассмотрим проблему внимательнее. Сервер, получив TPDU, либо сначала шлет подтверждение, а затем записывает полученное TPDU в буфер приложения, либо сначала записывает, а потом шлет подтверждение. Если сервер упал, послав подтверждение, но до того, как он осуществил запись, то клиент будет находиться после восстановления сервера в состоянии S0, хотя подтвержденное TPDU потеряно. Пусть, наоборот, сервер сначала записал TPDU, а потом упал. Тогда после сбоя сервер найдет клиента в состоянии S1 и решит, что надо повторить неподтвержденное TPDU. В результате получим повторное TPDU. Можно формально показать, что эта проблема только средствами транспортного уровня не решается. Надо, записав TPDU, информировать об этом приложение и только после этого слать подтверждение. При восстановлении надо опрашивать не только клиента на транспортном уровне, но и приложение. 6.6.3. Транспортные протоколы в Internet: TCP и UDP В Internet есть два основных транспортных протокола: TCP - ориентированный на соединение и UDP - не ориентированный на соединение. Поскольку сервис, реализуемый протоколом UDP - это практически сервис, реализуемый протоколом IP, с добавлением небольшого заголовка, то основное внимание здесь мы уделим протоколу TCP. TCP (Transmission Control Protocol) - специально созданный протокол для надежной передачи потока байтов по соединению «точка-точка» через ненадежную сеть. ТСР был сознательно разработан так, чтобы он мог адаптироваться к условиям и особенностям разных сетей, устойчиво и эффективно функционировать в условиях internet (нескольких сетей). На каждой машине, поддерживающей ТСР, есть ТСР-агент, который располагается либо в ядре ОС, либо в процессе пользователя, который управляет ТСР-потоками и доступом к сервису IP-протокола. ТСР получает поток данных от прикладного процесса, дробит их на сегменты не более чем по 65 Кбайт (на практике не более 1,5 Кбайт) и отправляет их как отдельные IP-пакеты. Протокол ТСР подробно описан в RFC 793, RFC 1122  и RFC 1323. Поскольку IP-уровень не гарантирует доставку каждого пакета, то в задачу ТСР входит определение потерь и организация повторной передачи потерянного. Поскольку на сетевом уровне в Internet соединения не поддерживаются, то сегменты могут поступать к получателю в неправильном порядке и задача ТСР - восстановить этот порядок. 6.3.1. Модель сервиса TCP Доступ к ТСР-сервису происходит через сокет. Сокет состоит из IP-адреса хоста и 16-разрядного локального номера на хосте, называемого порт. Сокеты создаются как отправителем, так и получателем. Порт - это TSAP для ТСР. Каждое соединение идентифицируется парой сокетов, между которыми оно установлено. Один и тот же сокет может быть использован для разных соединений. Никаких дополнительных виртуальных соединений не создается. Порты с номерами до 256 зарезервированы для стандартного сервиса. Например, если надо обеспечить FTP-передачу файла, то надо соединяться через 21-й порт, где находится FTP-демон. Для TELNET - через 23-й порт. Полный список таких портов можно найти в RFC 1700. Все ТСР-соединения - дуплексные, т.е. передача идет независимо в оба направления. ТСР-соединение поддерживает только соединение «точка-точка». Не существует ТСР-соединений от одного ко многим. ТСР обеспечивает поток байтов, а не поток сообщений. Напомним, это значит, что границы сообщений не поддерживаются автоматически в потоке. После того, как приложение передало данные ТСР агенту, эти данные могут быть отправлены сразу на сетевой уровень, а могут быть буферизованы, как поступить - решает ТСР-агент. Однако в ряде случаев надо, чтобы они были отправлены сразу, например, если эти данные представляют собой команду для удаленной машины. Для этого в заголовке ТСР-пакета есть флаг PUSH. Если он установлен, то это говорит о том, что данные должны быть переданы немедленно. Наконец, последняя возможность ТСР-сервиса, которую здесь стоит упомянуть - срочные данные. Если для данных установлен флаг URGENT в заголовке, то все данные, накопленные в буфере к моменту появления события, вызвавшего этот флаг, по данному соединению передаются сразу и не буферизуются. Примером события, вызывающего такой флаг, является нажатие пользователем на удаленной абонтской машине комбинации Ctrl-Del. Когда срочные данные поступают к месту назначения, то получателю передают их немедленно. 6.3.2. Протокол TCP Каждый байт в ТСР соединении имеет 32-разрядный номер. В сети с пропускной способностью 10 Мбит/сек. потребуется не менее часа, чтобы исчерпать все номера с 0 до 232. Эти номера используются как для уведомления, так и в механизме управления окнами.   ТСР-агенты обмениваются сегментами данных. Каждый сегмент имеет заголовок от 20 байтов и более (по выбору) и тело переменной длины. Один сегмент может включать байты от разных отправителей, а может включать часть данных одного. Какой длины может быть тело, решает ТСР-агент. Длину сегмента ограничивают два фактора. Во-первых, длина сегмента не должна превышать максимальную длину IP-пакета - 64 Кбайт. Во-вторых, каждая сеть имеет максимальную единицу передачи - MTU (maximum transfer unit), и каждый сегмент должен помещаться в MTU. В противном случае маршрутизаторам придется применять фрагментацию. При этом возрастают накладные расходы на передачу в сети, так как каждый фрагмент оформляется как самостоятельный пакет (с 20-байтным заголовком). Основным протоколом, который используется ТСР-агентом, является протокол скользящего окна. Это значит, что каждый посланный сегмент должен быть подтвержден. Одновременно с отправлением сегмента взводится таймер. Подтверждение придет либо с очередными получаемыми данными, если они поступят, либо в специальном сегменте-подтверждении. Подтверждение будет иметь порядковый номер очередного ожидаемого получателем сегмента. Если таймер исчерпается прежде, чем придет подтверждение, то сегмент посылается повторно. Несмотря на кажущуюся простоту, ТСР-протокол достаточно сложен и должен решать следующие основные проблемы: восстанавливать порядок сегментов убирать дубликаты сегментов, в каком бы виде они не поступали определять разумную задержку для time_out для подтверждений в получении сегмента устанавливать и разрывать соединения надежно управлять потоком управлять перегрузками Рисунок 6-18. (а) 4 сегмента по 512 байт отправлены как отдельные IP-дейтаграммы; (b) 2048 байт данных доставлены к приложению за один вызов READ 6.3.3. Заголовок сегмента в TCP Заголовок сегмента в ТСР показан на рисунке 6-19. Максимальная длина раздела данных – 65 495 байтов. Поля Source port и Destination port указывают номера портов на стороне отправителя и получателя соответственно.   Sequence number и Acknowledgement number содержат порядковый номер ожидаемого байта и следующего ожидаемого, а не последнего полученного байта. Поле ТСР-заголовок указывает на длину ТСР-заголовка в 32 разных словах.  Дело в том, что поле Options (параметры) может иметь переменную длину, а вместе с ним и вся длина зголовка ТСР станет переменной. Рисунок 6-19. Заголовок TCP 6-битное поле флагов.   Бит Urg используется вместе с полем Urgent pointer, которое указывает на начало области срочных данных. ACK - 1, если поле Acknowledgement number используется, в противном случае – 0. PSH - 1, если отправитель просит транспортного агента на стороне получателя сразу передать эти данные приложению и не буферизовать их. RST – используется, чтобы переустановить соединение, которое по какой-либо причине стало некорректным. Получение пакета с таким флагом означает наличие проблемы, с которой надо разбираться. SYN – 1, при запросе на соединение. Флаг ACK указывает на наличие или отсутствие подтверждения. SYN=1 ACK=0 – запрос на соединение, SYN=1 ACK=1 – подтверждение соединения. FIN - запрос на разрыв соединения. У отправителя нет больше данных. Поле Window size используется алгоритмом управления окном. Поле Options используется для установления возможностей, не предусмотренных стандартным заголовком. Например, здесь часто указывается максимальный размер поля данных, допустимый по данному соединению. Это поле имеет переменную длину. Рисунок 6-20. Псевдозаголовок, включенный в контрольную сумму 6.3.4. Управление соединениями в TCP Как уже было сказано, установление ТСР-соединения происходит по протоколу трехкратного рукопожатия. Флаги SYN и ASK в заголовке сегмента используются для реализации примитивов CONNECTION REQUEST и CONNECTION ACCEPTED. Флаг RST используется для реализации примитива REJECT. Это означает, что указанные выше примитивы вызывают посылку ТСР-пакета с установленным соответствующим флагом. На рисунке 6-21 (а) показаны состояния при установлении соединения. Когда приходит запрос на соединение по определенному порту, транспортный агент проверяет, есть ли процесс, который выполнил примитив LISTEN на этом порту. Если такой процесс есть, то ему передается управление. Если такого процесса нет, то в ответ идет отказ от установления соединения. Рисунок 6-21. Установление TCP-соединения Случай (b) показывает ситуацию, когда два хоста одновременно пытаются установить соединение между двумя одинаковыми сокетами (коллизия). Поскольку каждое соединение идентифицируется парой сокетов, то будет установлено только одно из соединений. Таймер для последовательных номеров сегментов тактируется с частотой 4 мксек., максимальное время жизни пакета - 120 сек. Напомним, что начальный номер сегментов никогда не равен нулю, по соображениям, приведенным ранее. Для генерации последовательных номеров сегментов используют механизм логических часов. ТСР-соединение, как уже говорилось, - дуплексное, т.е. в каждом направлении данные передаются независимо и соединение разрывается независимо по каждому направлению. Поэтому лучше всего представлять его как два симплексных соединения. Если в очередном сегменте флаг FIN=1, то в этом направлении данных больше не будет. При получении подтверждения для этого сегмента соединение в этом направлении считается разорванным. В другом направлении передача может продолжаться сколь угодно долго. Если подтверждения на первый FIN нет в течение двух интервалов жизни пакетов, то по time-out соединение считается разорванным. Противоположная сторона также по истечении этого периода времени узнает, что никто от нее не ждет ответа. В таблице 6-22 и на рисунке 6-23 представлена процедура установления и разрыва соединения в виде диаграммы конечного автомата. Таблица 6-22. Состояния, используемые в конечном автомате управления TCP-соединениями Состояние Описание CLOSED Нет активных или ожидающих соединений LISTEN Сервер ожидает входящего вызова SYN RCVD Запрос на соединение доставлен; ожидание подтверждения SYN SENT Приложение открывает соединение ESTABLISHED Состояние нормальной передачи данных FIN WAIT 1 Приложение сообщило об окончании работы FIN WAIT 2 Другая сторона согласилась разорвать соединение TIMED WAIT Ожидание, пока все пакеты прекратят свое существование CLOSING Попытка обоих сторон одновременно закрыть соединение CLOSE WAIT Противоположная сторона инициировала разрыв LAST ACK Ожидание, пока все пакеты прекратят свое существование Рисунок 6-23. Конечный автомат управления TCP-соединениями 6.3.5. Стратегия передачи в TCP Управление окнами в протоколе ТСР, как в управлении потоком на канальном уровне, не связано прямо с поступлением подтверждений. Предположим, что у получателя есть буферы в 4096 байт, как показано на рисунке 6-24. Если отправитель послал сегмент в 2048 байт, то получатель, получив и подтвердив этот сегмент, будет показывать окно в 2048 байт до тех пор, пока приложение не возьмет часть данных из полученного сегмента в 2048 байт. Отправитель посылает следующие 2048 байт. Теперь размер окна равен 0 байт. Рисунок 6-24. Управление окнами в TCP Когда поле WIN=0, отправитель может послать сегмент в двух случаях. Первый - если это данные URGENT. Например, когда требуется убить процесс на удаленной машине. Второй - если это однобайтовый сегмент. Это может потребоваться, чтобы заставить получателя показать текущее состояние буфера, что очень важно, так как позволяет обойти тупик. Заметим, что протокол ТСР не требует от агента-отправителя сразу передавать сегмент, как только данные поступили от приложения. Эту свободу ТСР использует, чтобы повысить свою производительность. Рассмотрим, к примеру, удаленный редактор TELNET. Если передавать по сети каждое движение пользователя мышкой или нажатие им клавиши на клавиатуре, то обмен будет очень не эффективным. На передачу одного символа будет приходиться передача сегмента в 160 байт. Поэтому часто при реализации протокола ТСР вводят специальную задержку на посылку подтверждения и состояния окна, чтобы дать отправителю накопить буфер для отправки. Другую стратегию предложил Нагл (Nagle) – если работа идет с приложением, которое генерирует однобайтные сообщения, то надо первый байт послать, а все остальные буферизовать до тех пор, пока не придет подтверждение на посланный байт. Все буферизованные байты нужно послать одним сегментом, после чего буферизовать все байты, пока не придет подтверждение на посланный сегмент. Алгоритм Нагла работает хорошо. Однако есть приложения, где его следует отключить, – X-Windows. Здесь перемещения мыши по экрану надо пересылать сразу без буферизации. Другая проблема, которая может существенно понизить производительность протокола ТСР – т.н. «синдром дурацкого окна» (рисунок 6-25). Он возникает, когда приложение на стороне отправителя передает ТСР-агенту данные большими блоками, а приложение на стороне получателя читает данные побайтно! В этой ситуации может произойти следующее. Буфер на стороне получателя полон и отправитель знает об этом. Приложение-получатель считывает один байт из буфера. ТСР-агент получателя радостно шлет сообщение о доступном буфере в один байт. Отправитель обязан послать один байт. После чего буфер получателя опять полон, и т.д. Кларк предложил запретить сообщать получателю в таких случаях об освободившемся месте на один байт. Получателя принуждают ждать, пока либо не освободится размер, равный максимальной длине сегмента, объявленной при установлении соединения, либо не освободится половина буфера. Отправитель также должен стараться избегать крохотных сегментов, а посылать большие. У ТСР-агента получателя также есть средства улучшить производительность соединения. Например, можно запретить приложению использовать примитив READ до тех пор, пока у агента есть данные в буфере. Конечно, такое решение увеличит время отклика, но для неинтерактивных приложений это не страшно. Другая проблема для получателя, понижающая производительность соединения, - нарушение порядка поступления сегментов. Например, если поступили сегменты 0, 1, 2, 4, 5, 6, 7, получатель может подтвердить сегменты 0-2, забуферизовать сегменты 4-7. Тогда отправитель по time-out перешлет сегмент 3, после чего получатель подтвердит 4-7 сегменты. Рисунок 6-25. «Синдром дурацкого окна» 6.3.6. Управление перегрузками в TCP Здесь мы рассмотрим, как протокол ТСР борется с перегрузками. В основе всех методов лежит принцип сохранения количества пакетов: не посылать новый, пока старый не покинет сеть, т.е. не будет доставлен. Основная идея очень проста - при возникновении перегрузки не посылать новых пакетов. В протоколе ТСР это реализуется динамически с помощью механизма окон. Прежде всего, протокол ТСР обнаруживает перегрузку по росту числа time_out. Если эта величина превышает некоторый предел, являющийся параметром протокола, то это фиксируется как перегрузка. Причин может быть две – шум в канале и сброс пакетов маршрутизатором. Различить их сложно. В наши дни каналы достаточно надежные, так что актуальной остается вторая причина. На рисунке 6-26 дана иллюстрация перегрузок. Перегрузки возникают по двум причинам: нехватка буфера на стороне получателя – недостаточная емкость получателя (а); перегрузка внутри сети – недостаточная емкость сети (b). Рисунок 6-26. Причины перегрузок В Internet эти ситуации различаются как внутренняя емкость сети и емкость получателя. Поэтому каждый отправитель поддерживает два окна - обычное окно отправителя и окно перегрузки. Каждое показывает количество байтов, которое отправитель может послать. Фактически отправляемое количество байтов - минимум из этих двух величин. Сначала окно перегрузки полагают равным размеру максимального сегмента для данного соединения. Если сегмент успешно (без time_out) был передан, то окно перегрузки увеличивается вдвое. Это увеличение будет происходить до тех пор, пока либо не наступит time_out и произойдет возврат к предыдущему значению, либо размер окна перегрузки не достигнет размера окна получателя. Этот алгоритм называется slow start - медленный старт. Другой параметр управления перегрузками в Internet – порог (threshold). Алгоритм медленного старта при возникновении перегрузки устанавливает этот параметр равным половине длины окна перегрузки, а окно перегрузки - равным размеру максимального сегмента. Окно перегрузки растет экспоненциально до тех пор, пока не сравняется с порогом, после чего оно растет линейно, пока не достигнет размера окна получателя. На этом рост прекращается до первой перегрузки. Работа этого алгоритма показана на рисунке 6-27. Рисунок 6-27. Алгоритм управления перегрузками в internet 6.3.7. Управление таймером в TCP Протокол ТСР использует несколько таймеров для управления передачей. Наиболее важный из них - таймер повторной передачи. Этот таймер устанавливают, когда отправляют сегмент. (Напомним, что так мы называем TPDU-пакет.) Если подтверждение пришло до исчерпания этого таймера, то его останавливают и сбрасывают. Если таймер исчерпан, то сегмент посылают повторно. Здесь основная проблема - как удачно выбрать величину time_out: временной интервал, по истечении которого сегмент надо передать повторно. Как мы знаем, эта проблема встречается и на других уровнях. Однако на транспортном уровне она имеет особенность, которая заключается в следующем. На канальном уровне дисперсия величины задержки подтверждения имеет ярко выраженный максимум (см. рисунок 6-28 (a)). Другими словами, ее разброс невелик. Величину time_out на этом уровне устанавливают чуть больше ожидаемой величины прихода подтверждения. На транспортном уровне функция распределения величины задержки подтверждения имеет более пологую форму, чем на канальном уровне (см. рисунок 6-28 (b)). Поэтому предсказать величину времени, которая нужна для передачи данных от источника до получателя и передачи подтверждения от получателя до источника, очень трудно. Заведомо эта величина не должна быть постоянной в силу гладкости функции распределения. В основе используемого в протоколе ТСР алгоритма, предложенного Якобсоном в 1988 году, лежит специальная переменная RTT для получения оптимального значения величины time_out (Round Trip Time), значение которой постоянно модифицируется. В этой переменной хранится наименьшее время подтверждения. При каждой передаче сегмента замеряется величина задержки подтверждения М. Если при очередной передаче подтверждение поступило прежде, чем наступил time_out, значение переменной RTT немного уменьшают, в противном случае - увеличивают по формуле: RTT = ?RTT + (1- ?)M, где ?=0,87. Однако, даже зная величину RTT, определить величину ожидания оказалось непросто. Якобсон предложил вычислять величину D - отклонения между ожидаемой величиной задержки и измеренной по формуле: D = ?D + (1-?) |RTT - M|. Кроме этого, Якобсон показал как, зная D, вычислить величину time_out по формуле: time_out =  RTT + 4*D. Позднее Филл Карн (Phill Karn) модифицировал эту формулу для случая повторно передаваемых сегментов. Действительно, когда поступает подтверждение для сегмента, то не ясно, то ли это подтверждение для первой передачи, толи для последней. Филла интересовал вопрос вычисления величины RTT для передачи данных по протоколам TCP/IP по КВ-радиоканалу. В результате экспериментов он показал, что для повторно передаваемых сегментов эффективно просто удваивать величину ожидания до тех пор, пока подтверждение не поступит с первого раза. Другой важный таймер в протоколе ТСР - таймер настойчивости. Он позволяет бороться со следующего типа тупиками. Когда получатель посылает сообщение с нулевым размером окна, отправитель останавливает передачу и ждет сообщения об изменении размера окна. Наконец, получатель послал такое сообщение, но оно было потеряно. Все ждут. Чтобы избежать такой ситуации, используют таймер настойчивости. Если он исчерпан, то отправитель шлет сообщение получателю, напоминая ему о проблеме размера буфера. Еще один важный таймер - таймер функционирования. Если по какой-либо причине по соединению долго не посылали сообщений, то надо проверить, функционирует ли оно. Когда этот таймер исчерпан, то соответствующая сторона шлет другой стороне запрос: «Жива ли ты?» Если ответа не поступает, то соединение считается разорванным. Рисунок 6-28. Дисперсия величины задержки подтверждения: (а) На канальном уровне; (b) На транспортном уровне (TCP) 6.3.8. Протокол UDP Internet поддерживает также транспортный протокол без соединений - UDP. Протокол UDP (User Datagram Protocol) предназначен для обмена дейтаграммами между процессами компьютеров, входящих в единую сеть с коммутацией пакетов. В качестве протокола нижнего уровня UDP-протокол использует IP. Протокол UDP предоставляет прикладным программам возможность отправлять сообщения другим приложениям, используя минимальное количество параметров протокола. Этот протокол не обеспечивает достоверность доставки пакетов, защиты от дублирования данных или от сбоев в передаче. За исключением параметров приложения - номеров портов отправителя и получателя пакета, UDP практически ничего не добавляет к IP-дейтаграмме. Рисунок 6-29. Заголовок UDP Протокол UDP намного проще, чем TCP, и полезен в ситуациях, когда мощные механизмы обеспечения надежности протокола TCP не требуются или будут только помехой для решения определенного рода задач, например, аутентификации пользователей. Структура UDP-заголовка показана на рисунке 6-29. Source Port (16 бит). Порт отправителя. Это поле может содержать номер порта, с которого был отправлен пакет, когда это имеет значение (например, когда отправитель ожидает ответа). Если это поле не используется, оно заполняется нулями. Destination Port (16 бит). Порт назначения - это порт компьютера, на который пакет будет доставлен. Length (16 бит). Поле длины. Длина (в байтах) этой дейтаграммы, включая заголовок и данные. (Минимальное значение этого поля равно 8). Checksum (16 бит). Поле контрольной суммы. Контрольная сумма UDP-пакета представляет собой побитное дополнение 16-битной суммы 16-битных слов (аналогично TCP). В вычислении участвуют: данные пакета, заголовок UDP-пакета, псевдозаголовок (информация от IP-протокола), поля выравнивания по 16-битной границе (нулевые). Преимущество протокола UDP состоит в том, что он требует минимум установок и параметров для соединения двух процессов между собой. Этот протокол используется при работе Серверов Доменов (Name Servers), протокола TFTP (Trivial File Transfer), при работе с SNMP-протоколом и построении систем аутентификации. Идентификатор UDP в IP-заголовке - число 17. Более подробное описание протокола UDP можно найти в RFC-768. 6.3.9. TCP и UDP в беспроводных коммуникациях Теоретически ТСР не должен зависеть от того, над какой средой он работает – оптической или беспроводной. Однако на практике дело обстоит иначе. ТСР-протокол тщательно оптимизировали при разных предположениях, которые не выполняются в беспроводной среде. Так, например, предполагалось, что сетевая среда достаточно надежна и рост числа time_out – это результат перегрузки, а не потери пакетов. В беспроводной среде потеря пакета - дело частое. Поэтому применение протокола ТСР «в лоб» с протоколом Якобсона медленного старта лишь усугубит положение. Так, например, если потери составляют 20%, то при пропускной способности канала в 100 пакетов в секунду фактически будем иметь лишь 80 пакетов. Согласно алгоритму медленного старта, при увеличении числа time_out надо понизить скорость, скажем до 50 пакетов в секунду, что приведет к фактической скорости 40 пакетов. Поэтому, если увеличилось число отказов в обычном канале, надо сбросить скорость, а если это число возросло в беспроводной среде, надо не понижать скорость, а слать пакеты повторно. Так что без знаний о среде принять решение трудно. Основным источником проблем является то, что в беспроводной среде соединения часто неоднородные. На рисунке 6-30 показан пример. Была предложена модификация ТСР – разбить такое соединение на два так, чтобы каждое стало однородным. Есть и другие решения, связанные с модификацией не самого ТСР, а канального уровня для базовых станций. Рисунок 6-30. Разбиение TCP-соединения на два 6.4. Вопросы производительности Производительность вычислительных сетей и машин один из основных показателей их эффективности. Сегодня настройка производительности сетей и систем больше искусство, чем наука. Многое из того, что здесь будет сказано – результат практики. Мы уже много внимания уделили вопросам производительности при рассмотрении, например, сетевого уровня. Однако вопросы производительности сети в целом, как системы, относятся к транспортному уровню и будут рассмотрены здесь. В последующих пяти разделах мы рассмотрим пять основных аспектов производительности сетей: 1.Проблемы производительности 2.Измерение производительности 3.Влияние организации сетей на производительность 4.Быстрая обработка TPDU 5.Протоколы для высокопроизводительных сетей 6.6.4.1. Проблемы производительности в сетях Одна из таких причин – перегрузки из-за несбалансированности ресурсов и нагрузки. Если на маршрутизаторы наваливается больше нагрузки, чем они могут переработать, возникает перегрузка. Перегрузки были достаточно подробно рассмотрены ранее. Производительность может падать из-за структурной несбалансированности ресурсов. Например, если персональную машину подключить к гигабитному каналу, то она будет захлебываться от наплыва пакетов из-за несбалансированности скорости процессора и скорости канала. Перегрузка может быть вызвана синхронно, как реакция на некоторые действия в сети. Это так называемые синхронные перегрузки. Например, если сегмент TPDU содержит неверный параметр (номер порта или процесса), то в ответ пойдет сообщение об ошибке. Если такой пакет получили сотни или тысячи машин, то в ответ последует ураган сообщений. Этой проблеме был подвержен протокол UDP до тех пор, пока не было внесено изменение в протокол, разрешающее или запрещающее слать подтверждение. Другой пример – перезагрузка машин в сети после сбоя питания. Все машины разом ринутся на RARP-сервер и файл-сервер за надлежащей информацией. В результате произойдет коллапс серверов. Другая причина – несоответствующая настройка системы. Например, если машины в сети имеют достаточно мощные процессоры и достаточно памяти, но под буфера в системе памяти выделено мало. В результате пакеты будут теряться из-за переполнения буферов. Аналогично, если планировщик процессов в операционной системе не дает достаточно высокий приоритет процессу обработки TPDU, то пакеты будут теряться. Другой параметр настройки – время time_out. Если time_out на подтверждение слишком короток, то много будет повторных посылок, если велик – скорость передачи упадет. Еще один пример - время ожидания попутного сообщения, с которым можно отправить подтверждение о полученном пакете. Если значение этого параметра мало, то будет много дополнительных пакетов-уведомлений. Если велико, то получатель может генерировать запросы по time_out и скорость передачи упадет. Появление гигабитных сетей принесло новые причины потери производительности. Пусть мы хотим передать пакет из Москвы во Владивосток, и у  нас есть  линия  на  1 Гбит/сек., а у получателя - буфер на 64 Кбайт. Задержка на передачу в одном направлении по оптоволоконному каналу будет около 20 мсек. Через 500 мксек все 64 Кбайт будут в канале, и отправитель будет ждать подтверждения, которое придет не ранее чем через 40 мсек с момента отправки первого байта сообщения. В результате пропускная способность канала будет использована на 1,25%! Дело в том, что буфер у получателя надо устанавливать равным произведению пропускной способности канала на величину задержки. В данном примере эта величина равнялась бы 40 млн.бит. А на практике он должен быть даже чуть больше. Получатель по какой-либо причине может не сразу отреагировать на поступившие данные. Рисунок 6-31. Состояние передачи 1 мегабита из Сан Диего в Бостон: (a) t=0; (b) После истечения 500 мксек; (c) После истечения 20 мсек; (d) После истечения 40 мсек 6.4.2. Измерение производительности в сети Когда пользователи сети обнаруживают падение производительности их приложений, они идут к администратору сети с жалобами. Последний обязан выяснить, что случилось, и принять необходимые меры. Типичная последовательность действий при исправлении производительности сети такова: 1.Измерить надлежащие параметры сети и производительность 2.Постараться понять, что происходит 3.Изменить один параметр Эти шаги надо повторять до тех пор, пока либо не удастся повысить производительность, либо не станет ясно, что имеющимися ресурсами этого сделать нельзя. Измерения можно проводить в разных местах и разными способами. Основная идея всех измерений состоит в том, чтобы запустить какую-то активность и измерить, как долго она продолжается, какие события ее сопровождают. Измерение длительности и сбор информации о событиях таят много подвохов. Ниже перечислены лишь некоторые из них. Количество испытаний должно быть достаточно велико. Измерить время доставки одного сегмента TPDU не достаточно. Это надо проделать миллионы раз. Тогда вычисление среднего и дисперсии будет свободно от влияния случайных факторов. В курсе математической статистики можно посмотреть, как выполнять такие вычисления. Выборка испытаний должна быть представительной. Проводить испытания надо в разное время дня и года. Что толку измерять производительность сети в университете в августе? Если измерения проводятся с 12 до 14, они опять-таки они не точны. В это время часто уходят на обед. Надо учитывать разрешающую способность часов. Как правило, таймер машины работает от сети 50 Гц. Поэтому измерять моменты наступления событий, происходящих чаще, чем через 20 мсек., им нельзя. Однако, если измерить интервал, когда произошло миллион регулярных событий, то, вычислив среднее, мы получим нужное значение. Ничего неожиданного во время измерений происходить не должно. Необходимо быть уверенным, что измерения происходят при «типичных» нагрузках. Нет единичных всплесков активности, например, лабораторных работ. Нельзя быть уверенным, что все «тихо» только потому, что измерения происходят в 3 часа утра. Хотя бы потому, что программа архивации работает обычно именно по ночам. Кэш-память может разрушить ваши измерения. Пусть, например, мы хотим измерить время передачи файла. Для этого надо будет его открыть, передать, закрыть и измерить общее время. Сделать это надо будет много раз. Однако если файл меньше размера кэш-памяти, то мы будем измерять скорость кэш-памяти, и только первое измерение будет показывать производительность сети. Чтобы избежать этого эффекта, надо выбирать файлы достаточно большого размера. Аналогичное влияние может оказывать буферизация. Например, если UDP получает подтверждение от сетевого уровня, как только сетевой уровень получил пакет, и на сетевом уровне есть буфер на 1000 дейтаграмм, то, проведя 999 испытаний, мы получим скорость передачи выше, чем пропускная способность физического канала. Нужно четко осознавать, что вы измеряете. Когда вы измеряете время чтения удаленного файла, то ваши измерения зависят от сетевой среды, операционных систем клиента и сервера, их сетевых плат, драйверов и т.п. Если вы хотите настроить взаимодействие при конкретной конфигурации, то ваше измерение имеет смысл. Если эти измерения будут использованы для выбора сетевых карт, то собранные таким образом данные не годятся. Например, может оказаться, что драйвер, используемый в измеряемой конфигурации, работает отвратительно с выбранной сетевой платой. Надо быть очень осторожным при экстраполяции результатов. Проведя измерения при определенной нагрузке, надо быть очень осторожным при их экстраполяции. Во многих случаях предположение о линейной экстраполяции может быть неверным (рисунок 6-27). Рисунок 6-32. Время отклика как функция от нагрузки 6.4.3. Правила, улучшающие производительность Повышать производительность существующей сети можно лишь в определенных пределах. Куда большие возможности для этого есть при проектировании сети. Ниже перечислены некоторые правила, сформулированные исключительно на опыте создания многих сетей. Правило 1: Скорость процессора важнее, чем скорость сети. Опыт показал, что накладные расходы на работу операционной системы и стека протоколов значительно больше, чем накладные расходы при передаче по физическому каналу. Теоретически RPC (удаленный вызов процедуры) на Ethernet должно занимать не более 102 мксек. На практике редко его удается снизить до 1500 мксек. Основные задержки происходят в системном программном обеспечении. Аналогично, при использовании высокоскоростных каналов важно повысить скорость доставки байта до канала и его обработку при получении. Другими словами, удвоив скорость процессора на хосте, можно удвоить пропускную способность сети. Удвоив емкость сети, можно ничего не получить, так как узким местом будет программное обеспечение хоста. Правило 2: Понижай число пакетов, чтобы сократить накладные расходы программного обеспечения. При обработке TPDU часть накладных расходов приходится на обработку самого TPDU, например, заголовка пакета, а часть - на обработку байтов TPDU, к примеру, на подсчет контрольных сумм. Поэтому, посылая TPDU в 128 байт, мы в 32 раза увеличиваем накладные расходы, чем если бы мы использовали TPDU в 4 Кбайт. На нижних уровнях большое влияние имеют прерывания, сопровождающие поступление пакета. В RISC-процессорах они ломают конвейер, вызывают переключение контекста, изменение кэш-памяти и т.д. Заметим, что алгоритмы Нагла и Кларка для «дурацкого окна» сокращают именно число прерываний. Правило 3: Минимизируй переключение контекста. Минимизировать переключения контекста удается с помощью специальных процедур, позволяющих накапливать пакеты и сообщения в буферах, прежде чем передавать их процессу более высокого уровня. Пример переключения контекста при размещении процесса сетевого менеджера в адресном пространстве пользователя показан на рис.6-48. Основную неприятность переключения контекста составляет потеря кэша. Правило 4: Минимизируй число копий. Куда больший ущерб производительности, чем переключение контекста, наносит множественное копирование одного и того же пакета. Обычно пакет, принятый сетевым интерфейсом, буферизуют на сетевой плате, потом в ядре на сетевом уровне, затем в буфере транспортного уровня и лишь потом в буфере приложения! Хорошая операционная система буферизует пословно. Однако типично операция буферизации занимает на менее 5 команд на слово. При процессоре с 50 MIPS (миллионов команд в секунду) создание трех копий при пяти командах на 32-битовое слово потребует 75 нсек. на байт. Таким образом, максимальная скорость приема данных будет не более 107 Мбит/сек. Учитывая обработку заголовков, прерываний, переключение контекстов, получим не более 50 Мбит/сек. Очевидно, что и речи быть не может о работе с таким процессором на линии в 1 Гбит/сек. Отметим также, что 50 Мбит/сек. - это скорость без учета обращений в память. С учетом памяти она будет раза в три меньше, около 16 Мбит/сек. Правило 5: Увеличение пропускной способности не сократит задержку. Если вам нужна пропускная способность, вы можете купить ее, например, проложив еще один кабель. Однако это не сократит задержку при передаче. Это требует улучшения программного обеспечения стека протоколов, системного программного обеспечения и сетевых интерфейсов. Правило 6: Лучше избегать перегрузок, чем восстанавливаться после них. При перегрузках пакеты теряются, расходуется пропускная способность, возникают бесполезные задержки и т.п. Восстановление после всего этого требует времени и усилий. Поэтому лучше избегать их. Правило 7: Избегайте наступления time_out. Таймеры неизбежны в сетях. Однако использовать их надо очень аккуратно и минимизировать их исчерпание (time_out), поскольку это влечет выполнение специальных действий. Поэтому установка таймера требует тщательных измерений, а изменение начального значения надо делать осторожно и постепенно. Рисунок 6-33. Четыре контекстных переключателя при обработке пакета с помощью сетевого менеджера пространства пользователя 6.4.4. Быстрая обработка TPDU Вывод из вышесказанного один – основным препятствием для быстрой работы сети является программное обеспечение стека протоколов. Здесь мы рассмотрим некоторые способы ускорения работы этого программного обеспечения. Затраты на обработку TPDU делятся на затраты на обработку каждого TPDU и затраты на обработку каждого байта. Оба вида затрат могут и должны быть сокращены. Для ускорения обработки собственно TPDU можно использовать следующую идею. Основную долю TPDU-сегментов обрабатывают в режиме Established (см. рисунок 6-23). Поэтому важно максимально ускорить обработку TPDU в этом режиме. Для этого надо уметь быстро различать этот нормальный случай от остальных специальных, например, разрыва соединения. Рассмотрим пример. Для простоты будем предполагать, что транспортный агент расположен в ядре операционной системы (см. рисунок 6-34), хотя те же идеи применимы и для других случаев, например, когда транспортный агент является частью прикладного процесса или библиотечной функцией. На стороне отправителя прикладной процесс через программное прерывание передает TPDU транспортному агенту в ядре. Агент с помощью проверок определяет, во-первых, какой случай имеет место: нормальный – отправка TPDU или специальный – разрыв соединения, во-вторых, что оправляется регулярное TPDU, а не специальное, и в-третьих, что окно получателя имеет достаточный размер. Если все условия выполнены, то может быть запущен ускоренный процесс отправки. Рисунок 6-34. Быстрая обработка TDPU В нормальной ситуации заголовки последовательных TPDU сегментов почти одинаковы. Чтобы воспользоваться преимуществом этого факта, транспортный агент сохраняет прототип заголовка у себя при запуске процедуры быстрой обработки. Обычно это делается максимально быстро на регистровом буфере. Те поля TPDU, которые меняются, переписываются в буфере. Затем указатель на TPDU и указатель на тело данных передаются сетевому агенту на сетевой уровень. Там может быть применена та же схема, после чего сетевой агент передаст пакет на канальный уровень. Рассмотрим, как эта идея работает в случае TCP/IP. На рисунке 6-35 (а) показан заголовок TPDU. Светлым тоном выделены поля, которые меняют свои значения от сегмента TPDU к сегменту TPDU. Пять слов прототипа TPDU копируются в буфер, вычисляется контрольная сумма, увеличивается порядковый номер. IP-процедура, в свою очередь, копирует пять слов прототипа (см. рисунок 6-35 (b)) своего заголовка, заполняя соответствующими данными поля «Identification» и «Checksum». Рисунок 6-35. (а) TCP-заголовок; (b) IP-заголовок Теперь рассмотрим, что происходит на стороне получателя (правая часть рисунка 6-34). Прежде всего, транспортный агент на стороне получателя должен найти запись о соединении для поступившего сегмента TPDU. Для TCP-протокола эта запись может храниться в хеш-таблице. Ключом к этой таблице может служить информация о портах отправителя и получателя и их IP-адресах. Другой подход к поиску записи о соединении предложил Кларк – использовать последнюю использованную. Как показала практика, эта эвристика  работает хорошо. После этого выполняются проверки, чтобы убедиться, что мы имеем дело с нормальным случаем, т.е. нет попытки разрыва соединения, нет URGENT-флага, и т.п. Если мы в состоянии Established, данные копируются приложению, при этом вычисляется контрольная сумма. Если она правильная, то корректируется запись о соединении, формируется подтверждение о получении и посылается отправителю. Описанная здесь в общих чертах схема носит название предвидения заголовка. Она используется во многих реализациях. Две другие области ускорения – управление буферизацией и таймерами. Основная идея ускорения при управлении буферизацией – избегать излишнего копирования. Управление таймерами состоит в том, что, хотя таймер устанавливается для каждого TPDU, срабатывает он лишь для немногих TPDU. Общая схема, оптимизирующая работу с таймерами, заключается в следующем. Записи о таймерах связываются в список. В очередном элементе списка указывают, сколько тактов от срабатывания предыдущего таймера должно пройти, чтобы сработал текущий. Поэтому, если есть три таймера, которые должны сработать в моменты 3, 10 и 12, то список будет выглядеть, как 3, 7, 2 соответственно. При такой организации достаточно корректировать при каждом такте не все таймеры, а только первую запись в списке. Другой прием, оптимизирующий работу с таймерами, называется колесо времени. Он использует массив (см. рисунок 6-36), длина которого пропорциональна максимальной длине временного интервала, который может возникнуть при работе. Каждый элемент в этом массиве соответствует одному такту часов. Рисунок 6-36. Колесо времени 6.4.5. Протоколы для гигабитных сетей В начале 90-х стали появляться гигабитные сети, т.е. коммуникационное оборудование, способное работать на таких скоростях. Первой естественной реакцией специалистов было использовать на этом оборудовании те же протоколы, что и для обычных сетей. Однако сразу возникло довольно много неожиданных проблем. Здесь мы рассмотрим некоторые из них и направления для развития новых протоколов. Первая проблема – длина поля для последовательной нумерации. Например, 16- или 32-разрядные поля для  Ethernet 10 Мбит будут исчерпаны даже при нумерации каждого байта чуть менее чем за сутки. Здесь неявно предполагалось, что так долго пакет существовать в сети не может. При скорости 1 Гбит/сек. 32-разрядный счетчик будет исчерпан за 32 сек. Среднее время жизни пакета в Internet около 120 сек. Можно, конечно, нумеровать не каждый байт, как в протоколе TCP, а лишь пакеты, но это не намного облегчит положение. Причина этой проблемы в том, что предположение о времени жизни пакета, верное ранее, теперь, при 1 Гбит/сек., не верно. Вторая проблема возникает из-за того, что скорость передачи растет быстрее производительности процессоров. В начале 70-х годов скорость передачи была порядка 56 Кбит/сек., быстродействие процессоров - 1 MIPS, размер пакета - 1 Кб. Это означает, что пакеты поступали со скоростью 56 пакетов в секунду, на обработку одного пакета отводилось 18 мсек. или 18 000 команд процессора. Если оставлять 50% производительности под приложение, то получим 9000 команд на обработку одного пакета, что весьма неплохо. В наши дни, при скоростях передачи 1 Гбит/сек., производительности процессора 100 MIPS, размере пакета 4 Кб, пакеты следуют со скоростью 30 000 в секунду, т.е. на обработку одного пакета остается не более 15 мсек., если мы половину производительности хотим оставить для приложения. За 15 мсек. процессор со скоростью 100 MIPS успеет сделать 1500 команд. При этом надо учитывать, что функционально команда RISC-процессора беднее команды CISC-процессора. Вывод: на работу сетевого программного обеспечения остается все меньше времени, следовательно, протоколы должны становиться проще! (Не забывайте каналы становятся надежнее.) Третья проблема состоит в том, что гигабитный канал принципиально отличается от мегабитного. Гигабитный канал более чувствителен к величине задержки, чем к скорости передачи. На рисунке 6-37 показана зависимость времени передачи от скорости передачи для файла в 1 Мб на расстояние 4000 км. Там хорошо видно, что, начиная с гигабитных скоростей, увеличение пропускной способности не дает существенного прироста в скорости передачи. Поэтому протоколы старт-стопного типа, например, RPC, существенно будут ограничены в производительности на таких каналах. То же самое можно сказать о протоколах с откатами (они применяются, например, на канальном уровне, если часть пакетов в последовательности была утеряна). Откат увеличивает задержку в канале и увеличивает объем повторно передаваемой информации. Рисунок 6-37. Зависимость времени передачи от скорости передачи для файла в 1 Мб на расстояние 4000 км Четвертая проблема, о которой стоит упомянуть, - скорее проблема, вызванная новыми приложениями, чем проблема собственно протоколов. Это проблема приложений, для которых важно не среднее значение задержки, а минимальное отклонение от среднего, т.е. максимально равномерный поток данных. Примером таких приложений являются многие мультимедиа-приложения. Теперь рассмотрим, что означают гигабитные скорости для организации протоколов. В былые времена протоколы создавались прежде всего с учетом минимизации затрат пропускной способности для служебных целей: поля делали маленькими, насколько это было возможно, упаковывали их плотно в байты. Сегодня есть большой запас пропускной способности. Проблема – сложность сетевого программного обеспечения. Одна из возможных идей – создание сетевого сопроцессора, на который можно возложить исполнение только сетевого программного обеспечения. Однако по своей мощности он будет примерно равен основному процессору, что существенно удорожит систему. Кроме этого, что центральный процессор будет чем-то загружен, пока сетевой работает, - это миф. Если центральный и сетевой процессоры будут примерно равны по мощности, то их синхронизация и взаимодействие, в свою очередь, превратится в самостоятельную проблему, решение которой потребует самостоятельных накладных расходов. Важные последствия гигабитные скорости несут для проблемы обратной связи в сетевых протоколах. Например, ожидание подтверждений, даже групповых, как в протоколе скользящего окна, теперь очень невыгодно. Куда выгоднее заранее оговорить допустимую скорость и время, в течение которого эта скорость должна выдерживаться. Другой пример обратной связи – протокол медленного старта. В этом протоколе многократно ведется испытание пропускной способности сети. На гигабитных скоростях каждое испытание – это огромные накладные расходы. Теперь куда выгоднее заранее договориться о допустимой максимальной пропускной способности и заранее ее зарезервировать на все время сеанса. Теперь несколько слов об организации структуры пакета для гигабитной сети. Полей должно быть как можно меньше, чтобы сократить время обработки заголовка. Поля должны быть достаточно длинными – это значит, что они должны обеспечивать решение проблемы адресации, контроля времени жизни пакета в сети и т.п. Поля должны быть выровнены по слову, чтобы облегчить их обработку в процессоре. Поле данных должно быть переменной длины и достаточно большим, чтобы поддерживать эффективность операций, загрузку канала. Чем длиннее поле данных, тем короче заголовок при прочих равных условиях. Об этом не стоит забывать. Заголовок и поле данных должны иметь раздельные контрольные суммы. Во-первых, для того, чтобы можно было проверять корректность заголовка, не трогая данных. Во-вторых, чтобы можно было проверить заголовок до того, как начнется копирование данных в приложение. В третьих, копирование данных можно совместить с проверкой контрольной суммы для данных. Если же контрольные суммы совмещены, то совмещать копирование и проверку было бы не рационально, так как, если начать проверку вместе с копированием, то при ошибке в заголовке копирование будет ненужным.