6.4. Транспортные протоколы интернета: UDP

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

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


6.4.1. Основы UDP

В наборе интернет-протоколов есть протокол передачи пользовательских дейтаграмм (User Datagram Protocol, UDP). Это транспортный протокол без установления соединения, описанный в RFC 768. UDP позволяет приложениям отправлять инкапсулированные IP-дейтаграммы без установления соединений.

С помощью UDP передаются сегменты, состоящие из 8-байтного заголовка, за которым следует поле Payload. Заголовок показан на илл. 6.27. Два порта служат для идентификации сокетов на отправляющем и принимающем устройствах. Когда приходит пакет UDP, содержимое Payload передается процессу, связанному с портом назначения. Это связывание происходит при выполнении BIND (или похожего примитива). Это показано на илл. 6.6 применительно к TCP (в UDP процесс связывания происходит так же). Представьте, что порты — это почтовые ящики, арендуемые приложениями, чтобы получать пакеты. Мы поговорим о них подробнее при обсуждении TCP, который тоже использует порты. В сущности, весь смысл применения UDP вместо обычного IP заключается как раз в указании портов отправителя и получателя. Без этих двух полей

Илл. 6.27. Заголовок UDP

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

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

Поле UDP length состоит из заголовка и данных. Минимальная длина равна длине заголовка, то есть 8 байт. Максимальная длина составляет 65 515 байт — меньше, чем максимальное число, которое может поместиться в 16 бит, из-за ограничений на размер IP-пакета.

Необязательное поле Checksum служит для повышения надежности. Оно содержит контрольную сумму заголовка, данных и псевдозаголовка. При выполнении вычислений в поле Checksum устанавливается нулевое значение, а поле данных дополняется нулевым байтом, если его длина представляет собой нечетное число. Алгоритм вычисления контрольной суммы просто складывает все 16-разрядные слова в дополнительном коде, а затем вычисляет дополнение для всей суммы. В результате, когда получатель считает контрольную сумму всего сегмента, включая поле Checksum, результат должен быть равен 0. Если она не подсчитывается, ее значение равно 0 (настоящая нулевая контрольная сумма кодируется всеми единицами). Однако отключать функцию подсчета контрольной суммы глупо, за исключением одного случая — когда нужна высокая производительность (например, при передаче оцифрованной речи).

В случае IPv4 псевдозаголовок будет выглядеть так, как показано на илл. 6.28. Он содержит 32-разрядные IP-адреса отправителя и получателя, номер протокола для UDP (17) и счетчик байтов для UDP-сегмента (включая заголовок). В случае IPv6 он выглядит аналогично, хотя и с некоторыми отличиями. Включение псевдозаголовка в контрольную сумму UDP помогает обнаружить неверно доставленные пакеты, хотя это нарушает иерархию протоколов, так как IP-адреса в нем принадлежат IP-уровню, а не UDP-уровню. В TCP для контрольной суммы используется такой же псевдозаголовок.

Илл. 6.28. Псевдозаголовок, включаемый в контрольную сумму UDP

Наверное, стоит прямо сказать, чего UDP не делает. Итак, UDP не занимается управлением потоком данных, контролем перегрузки, повторной передачей после приема испорченного сегмента. Все это перекладывается на пользовательские процессы. Что же он делает? UDP предоставляет интерфейс для IP путем демультиплексирования нескольких процессов с использованием портов и необязательного сквозного обнаружения ошибок. На этом все.

Для процессов, которые должны управлять потоком, контролировать ошибки и временные интервалы, протокол UDP — это как раз то, что доктор прописал. Особенно это полезно в клиент-серверных ситуациях. Зачастую клиент отправляет короткий запрос серверу и надеется получить короткий ответ. Если запрос или ответ теряется, клиент по прошествии определенного временного интервала может попытаться еще раз. Это позволяет не только упростить код, но и уменьшить требуемое количество сообщений по сравнению с протоколами, которым требуется начальная настройка (например, TCP).

Служба имен доменов (Domain Name System, DNS) — это приложение, которое использует UDP именно так, как описано выше. Мы изучим его в главе 7. В двух словах, если программе нужно найти IP-адрес по имени хоста, например www.cs.berkeley.edu, она может отослать UDP-пакет с этим именем на сервер DNS. Сервер в ответ на запрос отправляет UDP-пакет с IP-адресом хоста. Не требуется никакой предварительной настройки, как и разрыва соединения после завершения задачи. По сети просто передаются два сообщения.


6.4.2. Вызов удаленной процедуры

В определенном смысле процессы отправки сообщения на удаленный хост и получения ответа очень похожи на вызов функции в языке программирования. В обоих случаях вы передаете один или несколько параметров и получаете результат. Это соображение навело разработчиков на мысль о том, что можно попробовать организовать запросно-ответное взаимодействие по сети, выполняемое в форме вызовов процедур. Такое решение позволяет упростить и сделать более привычной разработку сетевых приложений. Например, представьте себе процедуру запроса IP-адреса хоста, get_IP_address(host name), работающую посредством отправки UDP-пакетов на сервер DNS, ожидания ответа и отправки повторного запроса в случае наступления тайм-аута (если одна из сторон работает недостаточно быстро). Таким образом, все детали, связанные с особенностями сетевых технологий, скрыты от программиста.

Ключевая работа в этой области написана в 1984 году Бирреллом и Нельсоном (Birrell and Nelson). По сути, было предложено разрешить программам вызывать процедуры, расположенные на удаленных хостах. Когда процесс на устройстве 1 вызывает процедуру на устройстве 2, вызывающий процесс устройства 1 блокируется, и на устройстве 2 выполняется вызванная процедура. Информация от вызывающего процесса может передаваться в виде параметров и приходить обратно в виде результата процедуры. Передача сообщений по сети скрыта от программиста приложения. Такая технология известна под названием удаленного вызова процедур (Remote Procedure Call, RPC). Она легла в основу многих сетевых приложений. Традиционно вызывающая процедура считается клиентом, а вызываемая — сервером. Мы будем называть их так же.

Идея RPC состоит в том, чтобы сделать вызов удаленной процедуры максимально похожим на локальный вызов. В простейшем случае для вызова удаленной процедуры клиентская программа должна быть связана с маленькой библиотечной процедурой, клиентской заглушкой (client stub), которая представляет серверную процедуру в пространстве клиентских адресов. Аналогично сервер должен быть связан с процедурой под названием серверная заглушка (server stub). Эти процедуры скрывают тот факт, что вызов клиентом серверной процедуры не является локальным.

Реальные шаги, выполняемые при удаленном вызове процедуры, показаны на илл. 6.29. Шаг 1 заключается в вызове клиентом клиентской заглушки. Это локальный вызов процедуры, при котором параметры самым обычным образом помещаются в стек. Шаг 2 состоит в упаковке параметров клиентской заглушки в сообщение и в осуществлении системного вызова для его отправки. Упаковка параметров называется маршалингом (marshaling). На шаге 3 операционная система передает сообщение с клиентского устройства на сервер. Шаг 4 заключается в том, что операционная система передает входящий пакет серверной заглушке. На шаге 5 серверная заглушка вызывает серверную процедуру с распакованными параметрами. При ответе выполняются те же самые шаги, но передача происходит в обратном направлении.

Илл. 6.29. Этапы выполнения удаленного вызова процедуры. Серым цветом выделены заглушки

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

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

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

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

Третья проблема состоит в том, что не всегда можно распознать типы параметров по спецификации или по самому коду. В качестве примера приведем процедуру printf, у которой может быть любое число параметров (не меньше одного), и они могут представлять собой произвольную смесь различных целочисленных (int, short, long), а также символьных и строковых параметров, чисел различной длины с плавающей запятой и др. Задача удаленного вызова процедуры printf может оказаться практически невыполнимой из-за такой своеобразной толерантности языка С. Однако не существует правила, говорящего, что удаленный вызов процедур возможен, только если используется любой другой язык кроме С (С++). Это подорвало бы репутацию метода RPC среди программистов.

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

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

С точки зрения протоколов транспортного уровня UDP является хорошей основой для реализации RPC. В самом простом случае запросы и ответы можно отправлять в одном UDP-пакете, а обработка может быть очень быстрой. Однако для реализации этой идеи потребуются и другие механизмы. На случай потери запроса или ответа клиенту необходим таймер, отсчитывающий время до повторной отправки пакета. Обратите внимание, что ответ служит неявным подтверждением запроса, поэтому отдельное подтверждение не требуется. Иногда параметры или результаты могут превысить максимальный размер UDP-пакета. Тогда потребуется некий протокол, позволяющий «разбирать» большие сообщения перед отправкой и затем корректно собирать их заново. Если возможно пересечение многочисленных запросов и ответов (как при параллельном программировании), соответствие ответа запросу указывается с помощью специальной метки.

Проблема более высокого уровня связана с тем, что операция может не быть идемпотентной (то есть ее нельзя повторить без риска сбоя). В простом случае мы имеем дело с идемпотентными операциями, такими как DNS-запросы и ответы. Клиент может повторно передавать такие пакеты сколько угодно, ничем не рискуя, до тех пор пока не придет ответ. Пакет может не дойти до сервера, а ответ может потеряться — это неважно. В любом случае, когда ответ придет, он будет тем же (если, конечно, за это время не обновится база данных DNS). Но не все операции идемпотентны: например, если они обладают важными побочными эффектами вроде изменения счетчика. RPC для таких операций требует более сложной семантики: вызванная процедура не должна выполняться несколько раз. В таких случаях может понадобиться установка TCP-соединения и отправка запроса по TCP, а не по UDP.


6.4.3. Транспортные протоколы реального времени

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

Так возник транспортный протокол реального времени (Real-Time Transport Protocol, RTP). Он описан в RFC 3550 и сегодня широко используется в мультимедиа. Мы опишем два компонента транспортировки данных в реальном времени. Первый из них — протокол RTP, необходимый для пакетной передачи аудио и видео. Второй — обработка данных, необходимая для воспроизведения (в основном со стороны получателя). Эти функции входят в стек протоколов, представленный на илл. 6.30.

Илл. 6.30. (а) Положение RTP в стеке протоколов. (б) Вложение пакетов

RTP обычно работает поверх UDP (в операционной системе). Происходит это так. Мультимедийное приложение может состоять из нескольких аудио-, видео-, текстовых и некоторых других потоков. Они прописываются в библиотеке RTP, которая (как и само приложение) находится в пользовательской области. Библиотека мультиплексирует потоки и вставляет их в пакеты RTP, которые, в свою очередь, отправляются в сокет. На другом конце сокета (в операционной системе) генерируются UDP-пакеты, в которые помещаются RTP-пакеты. Они передаются протоколу IP для отправки по каналу (например, Ethernet). На хосте-получателе происходит обратный процесс. В конечном итоге мультимедийное приложение получает данные из RTP-библиотеки. Оно отвечает за воспроизведение. Стек протоколов для этого сценария показан на илл. 6.30 (а), система вложенных пакетов — на илл. 6.30 (б).

В итоге определить, к какому уровню относится RTP, непросто. Так как он работает в пользовательской области и связан с прикладной программой, он, несомненно, выглядит как прикладной протокол. С другой стороны, он является общим, независимым от приложения протоколом, который просто предоставляет транспортные услуги. С этой точки зрения он может показаться транспортным протоколом. Наверное, лучше всего дать ему следующее определение: RTP — это транспортный протокол, который случайно оказался на прикладном уровне, и именно поэтому мы говорим о нем в этой главе.


RTP — транспортный протокол реального масштаба времени

Базовая функция RTP — мультиплексирование нескольких потоков реального времени в единый поток пакетов UDP. Поток UDP можно направлять либо по одному, либо сразу по нескольким адресам. Поскольку RTP использует обычный UDP, маршрутизаторы не обрабатывают пакет каким-то особым образом, если только не включены функции QoS, свойственные для IP. В частности, нет никаких гарантий доставки, пакеты могут теряться, задерживаться, повреждаться и т.д.

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

Каждое поле RTP Payload может содержать множество сэмплов, которые кодируются так, как этого хочет приложение. Межсетевое взаимодействие в RTP обеспечивается за счет определения нескольких профилей (например, отдельных аудиопотоков), каждому из которых может соответствовать несколько форматов кодирования. Например, аудиопоток может кодироваться при помощи PCM (8-битными символами с полосой 8 кГц), дельта-кодирования, кодирования с предсказанием, GSM-кодирования, MP3-кодирования и т.д. В RTP имеется специальное поле заголовка, в котором источник может указать метод кодирования, но затем источник никак не влияет на этот процесс.

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

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

Заголовок RTP показан на илл. 6.31. Он состоит из трех 32-разрядных слов и некоторых возможных расширений. Первое слово содержит поле Version, которое уже имеет значение 2. Будем надеяться, что текущая версия близка к окончательной, поскольку здесь осталась только одна кодовая точка (впрочем, 3 может обозначать, что настоящий номер версии содержится в слове расширения).

Илл. 6.31. Заголовок RTP

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

Поле СС сообщает, сколько сотрудничающих источников формируют поток. Их число может колебаться от 0 до 15 (см. ниже). Бит М — это маркер, связанный с конкретным приложением. Он может использоваться для обозначения начала видеофрейма, начала слова в аудиоканале или еще для чего-то важного и понятного для приложения. Поле Payload type (Тип данных) содержит информацию об использующемся алгоритме кодирования (например, несжатое 8-битное аудио, MP3 и т.д.). Поскольку такое поле есть в каждом пакете, метод кодирования может изменяться прямо во время передачи потока. Sequence number (Порядковый номер) — это счетчик, который увеличивается на единицу в каждом пакете RTP. Он используется для выявления потерянных пакетов.

Timestamp (Временная метка) генерируется источником потока и служит для ­записи момента создания первого слова пакета. Метка времени помогает снизить эффект временных колебаний, или джиттер (jitter), на стороне получателя. Это происходит за счет того, что момент воспроизведения не зависит от времени прибытия пакета. Synchronization source identifier (Идентификатор источника синхронизации) позволяет определить, какому потоку принадлежит пакет. Это и есть используемый метод мультиплексирования и демультиплексирования нескольких потоков данных, следующих в виде единого потока UDP-пакетов. Наконец, Contributing source identifiers (Идентификаторы сотрудничающих источников), если таковые имеются, используются, когда конечный поток формируется несколькими источниками. В этом случае микширующее устройство является источником синхронизации, а в полях идентификаторов источников перечисляются смешанные потоки.


RTCP — управляющий транспортный протокол реального времени

У протокола RTP есть родственный протокол под названием управляющий транспортный протокол реального времени (Real-Time Transport Control Protocol, RTCP). Он описан в RFC 3550 вместе с RTP. В его задачи входит поддержка обратной связи, синхронизация, обеспечение пользовательского интерфейса, однако передачей медиаданных он не занимается.

Первая функция RTCP может использоваться для обратной связи по задержкам, колебаниям времени задержки (или джиттеру), пропускной способности, перегрузке и другим свойствам сети, сведения о которых сообщаются источникам. Полученная информация может приниматься во внимание кодировщиком для увеличения скорости передачи данных (что улучшит качество), когда состояние сети позволяет это сделать, или для уменьшения скорости при возникновении в сети каких-либо проблем. Благодаря постоянной обратной связи алгоритмы кодирования динамически настраиваются, чтобы обеспечивать наилучшее качество при текущих обстоятельствах. Например, пропускная способность при передаче потока может как увеличиваться, так и уменьшаться, и в соответствии с этим могут изменяться методы кодирования (MP3 может заменяться 8-битным PCM или дельта-кодированием). Поле Payload type сообщает получателю, какой алгоритм кодирования применяется для данного пакета, что позволяет менять их по мере необходимости.

Проблема обратной связи заключается в том, что сообщения RTCP рассылаются всем участникам. Если группа большая, то пропускная способность, которая требуется протоколу, резко возрастет. Чтобы этого не произошло, источники RTCP снижают скорость отправки своих сообщений так, чтобы все вместе они потребляли, скажем, 5 % пропускной способности, используемой для передачи медиаданных. А для этого каждый участник должен знать эту пропускную способность (эту информацию он может получить от отправителя) и общее число участников (которое он вычисляет на основании сообщений RTCP).

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

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

Более подробно о протоколе RTP можно прочитать в работе Перкинса (Perkins, 2002).


Буферизация и контроль джиттера при воспроизведении

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

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

Решение заключается в буферизации пакетов на стороне получателя перед их воспроизведением для снижения джиттера. На илл. 6.32 мы видим поток пакетов, прибывающих к адресату с достаточно большим джиттером. Пакет 1 передан с сервера в момент времени t = 0 с и доставлен в t = 1 с. Пакет 2 задерживается и прибывает через 2 с. По прибытии пакеты помещаются в буфер устройства клиента.

Илл. 6.32. Выравнивание выходного потока с помощью буферизации пакетов

В момент времени t = 10 с начинается воспроизведение. В буфере уже находятся пакеты с 1-го по 6-й, поэтому их можно доставать оттуда через равные интервалы и тем самым обеспечить равномерное воспроизведение. Как правило, равные промежутки времени использовать не обязательно, так как метки времени RTP содержат информацию о том, когда следует начать воспроизведение.

К сожалению, пакет 8 задерживается настолько, что не успевает прибыть к тому моменту, когда он должен быть воспроизведен. В таком случае возможны два варианта. Проигрыватель может пропустить 8-й пакет и перейти к следующим пакетам. Или же он может остановить воспроизведение, пока не придет 8-й пакет, и тем самым создать неприятную паузу в музыке или фильме. Если приложение работает в режиме реального времени (например, при звонке через интернет), то пакет, скорее всего, будет пропущен. Такие приложения не очень хорошо функционируют в режиме ожидания. В приложениях, работающих с потоковым мультимедиа, проигрыватель может на время остановить воспроизведение. Чтобы улучшить ситуацию, можно начать воспроизведение еще позже и использовать буфер большего размера. Проигрыватели потокового аудио и видео часто используют буферы размером около 10 с, чтобы все пакеты (кроме утерянных) успели прийти вовремя. Приложения, работающие в реальном времени (например, видеоконференции) и требующие быстрой ответной реакции, используют маленькие буферы.

Для равномерного воспроизведения очень важна задержка воспроизведения (playback point). Это время, в течение которого получатель ожидает медиаданные, прежде чем начать воспроизведение. Этот параметр зависит от джиттера. Различие между соединением с маленьким и большим джиттером показано на илл. 6.33. Средняя задержка может не слишком отличаться, но при большом джиттере может потребоваться задержка воспроизведения, захватывающая 99 % пакетов (намного больше, чем при маленьком джиттере).

Илл. 6.33. Джиттер: (а) большой; (б) маленький

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

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

Загрузка...