Введение в SIP для Java, C# и VB разработчиков
SIP — протокол создания сессий
SIP (Session Initiation Protocol) был разработан для соединения людей и устройств для выполнения ими (возможно длительного) обмена информацией. Существующие протоколы, такие как HTTP и SMTP, не были специально разработаны для такой существенной человеческой деятельности, и поэтому SIP был создан, чтобы заполнить этот пробел. Однако SIP многое заимствует из этих двук протоколов: от шаблона обмена сообщениями, формата сообщения и кодирования из HTTP до схемы URI из SMTP.
В 2002 году проверенная версия стандарта SIP была оформлена Инженерным советом Интернет (IETF) как RFC3261. Из-за открытого характера процесса стандартизации IETF и того, что SIP основан на текстовом интерфейсе и имеет много общих черт с существующими спецификациями, данный протокол было легко понять, расширить и реализовать.
С момента своего появления SIP получил поддержку в качестве посредника при обмене мгновенными сообщениями (наприме, Windows Messenger) и VoIP (большинство известных платформ, кроме Skype).
Модули
Архитектура программных средств SIP состоит из нескольких соединенных модулей:
- Агент пользователя (UA — User Agent) — модуль, который представляет конечного пользователя в клиентском устройстве. Он обычно работает в двух режимах: клиент агента пользователя (UAC — User Agent Client) отправляет исходные запросы и обрабатывает ответы; и сервер агента пользователя (UAS — User Agent Server) принимает запросы и отправляет ответы.
- Прокси серверы привлекаются в маршрутизации SIP сообщений до правильной конечной точки. Фиксирующие прокси иногда используют агентов пользователей в логической структуре, называемой Back-To-Back-User-Agent
- Серверы переадресации обеспечивают новый адрес или иной маршрут к получателю. Сервер может использовать сервер определения местоположения для сохранения информации о местоположении.
- Регистратор действует как текущее хранилище информации о привязке клиента к сети.
Агент пользователя имеет тенденцию располагаться на устройстве пользователя. Остальные модули обеспечивают необходимые службы поддержки во многих сценариях.
Сообщения
SIP сообщения бывают двух видов:
- запрос — отправляется от клиента к серверу и определяет операцию, запрашиваемую клиентом;
- ответ — отправляется от сервера к клиенту и доставляет информацию о статусе текущего запроса.
Запрос
SIP запрос характеризуется как метод, очень похожий на HTTP запрос, и является ‘глаголом’, так как запрашивает действия, которые должны быть выполнены другими агентами пользователей и серверами. RFC3261 определяет шесть методов (первые шесть в таблице 1), а последующие стандарты определяют остальные методы расширения (начиная с INFO).
Метод | Описание |
---|---|
INVITE | Используется для установки SIP сессии. Параметры сессии должны быть согласованы. |
REGISTER | Аунтефицирует агента пользователя и передает текущее местоположение в сети. |
BYE | Завершает открытую сессию. |
ACK | Подтверждает успешный ответ на INVITE. Третья часть “трехстороннего рукопожатия”. |
CANCEL | Отменяет открытый запрос. BYE может быть использован, чтобы отменить (разрушить) существующий запрос. |
OPTIONS | Запрашивает возможности корреспондентов. |
Методы расширения | |
INFO | Передает во время разговора связанную с сессией информацию. Используется редко. |
MESSAGE | Используется для передачи мгновенных сообщений. |
NOTIFY | Публикует результаты событий. Используется вместе с запросами SUBSCRIBE. |
PRACK | Подтверждение предварительного ответа (Provisional Response ACKnowledgment). Подтверждает получение предварительного ответа. |
PUBLISH | Публикует информацию о статусе. Используется для передачи статуса присутствия в службах мгновенных сообщений. |
REFER | Механизм передачи запроса кому-то, более подходящему для его выполнения. |
SUBSCRIBE | Используется для запроса получения будующих запросов NOTIFY и PUBLISH. |
UPDATE | Изменяет параметры сессии в середине разговора. |
Ответы
Сообщения SIP ответов всегда посылаются в ответ на запрос. С их помощью отправляются обновления статуса, подтверждения, инструкции и коды ошибок назад к UAC, который передает запросы. Ответы могут быть либо предварительными, либо окончательными, и каждый ответ должен быть идентифицирован по 3-значному коду.
Типы ответов
Было определено шесть классов ответов, выделенных в отдельные категории, используя 3-значный код. Первые пять заимствованы из HTTP, шестой является новым для SIP.
Класс | Описание |
---|---|
1xx Предварительный | Подтверждает прием запроса и продолжение обработки. Предварительные ответы на INVITE никогда не подтверждаются запросом ACK. |
2xx Успех | Запрос был получен, обработан и применён. |
3xx Перенаправление | Предоставляет информацию о местоположении или альтернативные службы для попыток соединения. |
4xx Отказ запроса | Запрос содержит ошибку или не может быть обработан сервером. |
5xx Отказ сервера | Сервер не в состоянии выполнить запрос из-за внутренней ошибки. |
6xx Глобальный отказ | Нет службы, которая могла бы выполнить запрос. |
Внутри каждого класса, цифровые коды ответов предопределены, некоторые из них скопированы из HTTP.
Код | Расшифровка | Описание |
---|---|---|
100 | Trying Продолжение попытки | Следующий узел получил запрос. |
180 | Ringing Посылка вызова | Попытка оповещения пользователя. |
182 | Queued Поставлен в очередь | Временно недоступен. Запрос поставлен в очередь, а не отклонён. |
200 | OK | Запрос выполнен успешно. |
301 | Moved Permanently Перемещен окончательно | Пользователь больше не доступен по адресу, указанному в URI запроса. |
302 | Moved Temporarily Перемещен временно | Повторить запрос на новый адрес, указанный в заголовке Contact. |
400 | Bad Request Плохой запрос | Невозможно понять или правильно обработать запрос. |
401 | Unauthorised Неавторизован | Запросу либо отказано в аунтефикации, либо требуется дополнительная информация. |
403 | Forbidden Запрещено | Сервер отказывается обработать запрос. Запрос не повторять. |
404 | Not Found Не найден | Сервер не может определить пользователя в своем домене. |
408 | Request Timeout Время ожидания истекло | Сервер не смог обработать запрос в разумные сроки. |
415 | Unsupported Media Неподдерживаемое медиа | Формат медиа не поддерживается сервером. |
480 | Temporarily Unavailable Временно недоступен | Вызываемый абонент временно недоступен. |
485 | Ambiguous Двусмысленный | Двусмысленный URI запроса. |
486 | Busy Here Занято | Вызываемый абонент в данный момент не хочет или не может принять входящий звонок. |
500 | Server Internal Error Внутренняя ошибка сервера | Сервер столкнулся с непредвиденным условием. |
513 | Message Too Large Слишком большое сообщение | Длина сообщения превысила заданное ограничение. |
603 | Decline Отклонен | Пользователь явно отказался принять запрос. |
Поле заголовка Warning
Поле заголовка Warning используется для передачи дополнительной информации о состоянии ответа. Заголовок определяет 3-значный код от 300 до 399, имя хоста, а текст предупреждения.
Warning: 307 isi.edu "Session parameter 'foo' not understood".
Анатомия сообщения
Каждое SIP сообщение начинается со стартовой строки с последующей за ней последовательностью заголовков, которая отделяется от тела сообщения последовательностью символов возврата каретки и перевода строки (CRLF).
- Стартовая строка — форматированная строка запроса для запросов или строка статуса для ответов.
- Заголовки — именованные атрибуты, передающие дополнительную информацию о сообщении.
- Раздельтельная строка — разделитель между заголовком и телом сообщения.
- Тело — бинарная или текстовая полезная нагрузка. Обычно это протокол описания сессий SDP или текст сообщения.
Стартовая строка, строки всех заголовков, а также разделительная строка заканчиваются последовательностью символов [CRLF].
Стартовая строка
Стартовая строка передает тип сообщения и версию протокола. И для запроса (строка запроса), и для ответа (строка статуса) стартовая строка содержит три элемента, разделенных пробелами.
- Строка запроса: содержит метод и URI и заканчивается версией протокола ("SIP/2.0").
INVITE sip:bob@897s.aarhus.com SIP/2.0
- Строка статуса: начинается версией протокола, после чего следует числовой код состояния, и завершается короткой текстовой расшифровкой.
SIP/2.0 200 OK
Заголовки
Заголовки имеют такой же формат как и у типовых HTTP заголовков. Все они состоят из регистронезависимого имени в кодировке ASCII и двоеточия, за которым следует значение, которое иногда идет в кодировке UTF8 и обычно регистрозависимо. В каждом заголовке может быть один и более параметров, разделенных точкой с запятой, добавленных к значению и передающих дополнительные теги и функции.
header-name: header-value(;parameter-name=parameter-value)*[CRLF]
Каждый заголовок может быть разделен на несколько строк, используя последовательности символов [CRLF][TAB или SPACE] (так называемое свёртывание). Более того, несколько заголовков с одинаковыми именами (например, Contact) могут появиться на отдельных строках или могут быть помещены в одну строку, разделенные запятыми. Например:
Contact: <sip:alice@atlanta.com>
Contact: <sip:alice1@chicago.com>
Может быть представлено так:
Contact: <sip:alice@atlanta.com>, <sip:alice1@chicago.com>
Или, используя свёртывание:
Contact: <sip:alice@atlanta.com>,
<sip:alice1@chicago.com>
Тело сообщения
Тело сообщения описывает сессию (используя SDP) или содержит непрозрачный текст или бинарные данные, содержащие полезную нагрузку, связанную с сессией (например, с MIME типом или форматом сообщения). Тело сообщения может содержаться как в сообщениях запросов, так и в сообщениях ответов.
Поля заголовков
В следующих примерах Алиса совершает вызов Боба, используя его SIP URI, 'sip:bob@897s.aarhus.com'. Боб отвечает Алисе откликом, уведомляющим об успехе. Сообщение запроса INVITE в примере содержит SDP сообщение, на которое должен быть получен ответ вместе с ответом «200 OK».
Все примеры кода довольно независимы от языка программирования. Для тех, кто заинтересован в дальнейшем изучении SIP, в конце приводится список библиотек для Java, .NET и C++. И с небольшими изменениями весь код должен работать с любым из них (в данных примерах автор использовал библиотеку Konnetic SIP для C#).
Создание сообщения запроса
- Добавить строку запроса, которая указывает на сообщение запроса INVITE к аккаунту 'sip:bob@897s.aarhus.com'.
Invite invite = new Invite(new SipUri("sip:bob@897s.aarhus.com"));
- Создать заголовок Via, который указывает получателю обратный путь.
invite.ViaHeaders.Add(new ViaHeaderField("122.181.8.8:11506", SipTransportProtocol.Udp));
- Создать адреса отправителя и получателя. В качестве SIP URI могут использоваться IP адреса, но рекомендуется применять полные доменные адреса. Возможно использование отображаемых имён. В целях безопасности заголовку From разрешается быть анонимным, если это необходимо.
invite.From.Uri = new SipUri("sip:bob@897s.aarhus.com"); invite.From.DisplayName = "Bob"; invite.From.Tag = "769122"; invite.To.Uri = new SipUri("sip:alice@ml99.odense.com"); invite.To.DisplayName = "Alice";
- Создать уникальные идентификаторы для вызова и разговора. CallId — это уникальное значение для сессии. Последовательность Sequence увеличивается в последующих запросах. Набор To, From и Call-ID обеспечивает уникальный ключ для вызова.
invite.CallId.CallId = "afh7989asdfhf@ml99.odense.com"; invite.CSeq.Sequence = 3434534; invite.CSeq.Method = SipMethod.Invite;
- Создать дополнительную контактную информацию об отправителе.
invite.ContactHeaders.Add(new ContactHeaderField( new SipUri("sip:alice2@vejle.com")));
- И наконец, добавить описание содержимого сообщения. В данном примере контент сообщения не рассматривается.
invite.ContentType.MediaType = "application"; invite.ContentType.MediaSubType = "sdp"; invite.ContentLength = 136;
Сообщение SIP запроса
В результате сообщение SIP запроса должно выглядеть подобно следующему:
INVITE sip:bob@897s.aarhus.com SIP/2.0
Via: SIP/2.0/UDP 124.191.8.8:11506
Max-Forwards: 70
To: Bob <sip:bob@897s.aarhus.com>
From: Alice <sip:alice@odense.com>;tag=769122
Call-ID: afh7989asdfhf@ml99.odense.com
CSeq: 3434534 INVITE
Contact: <sip:alice2@vejle.com>
Content-Type: application/sdp
Content-Length: 136
Создание сообщения ответа
Если вы помните, в данном примере Боб отвечает Алисе откликом, уведомляющем об успехе. Сообщение — пример ответа «200 OK».
- Создать строку статуса, которая показывает, что запрос был успешен.
Response okMessage = new Response(StandardResponseCode.Ok);
- Скопировать заголовок Via из сообщения запроса.
okMessage.ViaHeaders.Add(new ViaHeaderField("122.181.8.8:11506", SipTransportProtocol.Udp));
- Скопировать адресную информацию. Поля To и From остаются теми же, что и в оригинале, за исключением параметра tag в поле To. Они не меняются местами для ответа. Вы должны думать о них, как об оригинальных полях To и From.
okMessage.From.Uri = new SipUri("sip:bob@897s.aarhus.com"); okMessage.From.DisplayName = "Bob"; okMessage.From.Tag = "769122"; okMessage.To.Uri = new SipUri("sip:alice@ml99.odense.com"); okMessage.To.DisplayName = "Alice"; okMessage.To.Tag = "abgj67";
- Скопировать идентификатор из сообщения запроса.
invite.CallId.CallId = "afh7989asdfhf@ml99.odense.com"; invite.CSeq.Sequence = 3434534; invite.CSeq.Method = SipMethod.Invite;
- Добавить дополнительную контактную информацию. На этот раз для Боба.
okMessage.ContactHeaders.Add(new ContactHeaderField( new SipUri("sip:bob2@vejle.com")));
- И наконец, добавить описание контента.
okMessage.ContentType.MediaType = "application"; okMessage.ContentType.MediaSubType = "sdp"; okMessage.ContentLength = 132;
Должен отметить, что хорошая библиотека предоставит API, который автоматизируют большую часть шаблонного кода, показанного выше. Например, копирование полей из запроса в ответ может быть реализовано в библиотеке.
Сообщение SIP ответа
В результате сообщение SIP ответа должно выглядеть подобно следующему:
SIP/2.0 200 OK
Via: SIP/2.0/UDP 124.191.8.8:11506
To: Bob <sip:bob@897s.aarhus.com>;tag=abgj67
From: Alice <sip:alice@odense.com>;tag=769122
Call-ID: afh7989asdfhf@mel99.odense.com
CSeq: 3434534 INVITE
Contact: <sip:bob2@vejle.com>
Content-Type: application/sdp
Content-Length: 132
Пример сценария установления соединения
В этом разделе подробно описывается процесс вызова между теми же агентами пользователей SIP, что рассмотрены выше, и которые могут использовать те же структуры сообщений. Показаны три этапа успешного вызова: начальная сигнализация, создание медиасессии и, в конце, завершение вызова.
Установление сессии
- UA Алисы отправляет сообщение INVITE на SIP адрес Боба(т.е. 'sip:bob@897s.aarhus.com'). Содержимое сообщения — сообщение в протоколе SDP, описывающее ожидаемый обмен медиаданными.
- UA Боба принимает INVITE и отвечает сообщением «100 Trying».
- Затем UA пытается привлечь к себе внимание Боба и одновременно с этим отправляет Алисе сообщение «180 Ringing».
- Боб отвечает, и его UA отправляет сообщение «200 OK». «200 OK» содержит также подтверждающее SDP сообщение Боба.
- В завершение, UA Алисы подтверждает получение ответа OK с помощью запроса ACK.
- Медиапотоки установлены непосредственно между Алисой и Бобом.
На этот момент мы подготовили почву для другого протокола (например, RTP) для транспортировки медиаданных непосредственно между Алисой и Бобом без дальнейшего вмешательства SIP. Эта передача реализована на так называемом канальном или транспортном уровне, а SIP действует в рамках уровня управления и сигнализации.
Завершение сессии
В конце вызова SIP используется для завершения сессии. Если, например, вызов завершается Бобом, обмен будет выглядеть следующим образом:
- Боб кладет трубку, и его UA инициирует завершение сессии передачей Алисе запроса BYE.
- UA Алисы отвечает сообщением «200 OK».
В приведенном выше примере Алиса и Боб ведут обмен абстрактными медиаданными. Это может быть и голосовой вызов, и видео конференция, и сеанс обмена мгновенными сообщениями; процедура установления и закрытия соединения будет выглядеть точно так же.
API и библиотеки SIP
Список не является исчерпывающим, но по мнению автора статьи в нём представлено лучшее, с чем он сталкивался. Разумными критериями, используемыми для отбора хорошего SIP API, являются реализация поддержки строго типизированных полей заголовков, реализация SIP транзакций, инкапсуляция большинства внутренностей SIP.
Разумный критерий, используемый для хорошего SIP API, является ли он обеспечивает поддержку для строго типизи
Язык | Ссылка | Описание |
---|---|---|
Java | NIST's SIP Library | Справочный API от National Institute of Standards and Technology. |
C#, VB и т.д. | Konnetic's SIP .NET Library | Хорошо документированные низкоуровневые SIP и SDP стеки. |
C#, VB и т.д. | Microsoft's Unified Comms Server | Высокоуровневые SIP, SDP и RTP стеки. |
C++ | PJSIP SIP Stack | Лёгкий, но полностью реализованный и легко портируемый SIP стек. |
Инструменты тестирования
Инструменты тестирования имеют важное значение, поскольку соблюдение стандарта необходимо для любого приложения взаимодействующего с другими SIP приложениями и серверами.
Инструмент | Описание |
---|---|
SIPp | Генератор SIP трафика от HP. |
PROTOS | Приложение тестирования от университета Оулу, Финляндия. |
ETSI TS 102 027-2 | Список тестовых сценариев SIP вызовов. |
Torture Tests | “Пыточные” тесты. |
Wireshark | Пожалуй, лучший сетевой анализатор из доступных. |
Дополнительная литература
- H.Schulzrinne. (2010) Henning Schulzrinne. [Online]. http://www.cs.columbia.edu/~hgs/
- A.B.Johnston, SIP: Understanding the Session Initiation Protocol, 3rd ed. Boston, MA, USA: Artech House, 2007.
- J.Rosenberg и др., "SIP: Session Initiation Protocol" RFC3261 2002
- IETF. (2010) Multiparty Multimedia Session Control (mmusic). [Online]. http://datatracker.ietf.org/wg/mmusic/
- IETF. (2010) Session Initiation Protocol (SIP). [Online]. http://datatracker.ietf.org/wg/sip/
- H.Schulzrinne и др., "RTP: A Transport Protocol for Real-Time Applications" RFC3550 2003.
Некоторые из вышеперечисленных IETF нуждаются в комментариях. Не позвольте этому остановить вас. Большинство RFC написаны хорошо и доступно. Вы многое узнаете, просто прочитав первые 10 страниц.