Как стать автором
Обновить

Протоколы прикладного уровня: Jabber/XMPP часть1

Сетевые технологии
Прочитав статью и испробовав команды, научимся
--Соединяться с Jabber сервером
--Логиниться
--Менять статусы
--Отправлять сообщения
--Отключаться

И все это на чистом XML

В принципе, можно статью назвать «Введение в XMPP» или типа того… Но суть не изменится
Приступим-же!

Простая Jabber сессия представляет из себя следующую последовательность операций:
  • Соединение с сервером
  • Создание потока
  • Включение шифрования и создание нового потока в шифрованном канале (опционально)
  • Аутентификация
  • Привязывание (bind) потока к ресурсу (имя@сервер/ресурс)
  • Создание сессии
  • Рассылка статуса «доступен»
  • Отправка/получение сообщений, статусы, ростер, «визитные карточки», работа с сервисами и транспортами и т.п.
  • Рассылка статуса «отключен»
  • Закрытие потока
  • Отключение от сервера

Попробуем реализовать эту схему. В моем эксперименте используется сервер jabber.ru, полагается что аккаунт на нем у вас уже есть.
В листинге ниже все мои комментарии, которые располагаются внутри XML блока, находятся в тегах XML комментариев , весь остальной XML представлен без изменений (разве что добавлены переносы строк для читаемости). Если соберетесь вводить все это в консоли (это вполне реально, я сам так и делал), желательно предварительно скопировать весь код в текстовый редактор, заменить авторизац. данные, удалить комментарии и переводы строк.
Блок XML, отправленный клиентом обозначается --C:, переданный сервером --S:

Соединение с сервером


Соединяемся с сервером jabber.ru по 5222 порту
telnet jabber.ru 5222
Trying 213.180.203.19...
Connected to pluton.relax.ru.
Escape character is '^]'.

Создание потока


Отправляем серверу начало XML потока, в котором указан в том числе адрес сервера (jabber.ru)
--C:
<stream:stream xmlns="jabber:client" to="jabber.ru" version="1.0" xmlns:stream="http://etherx.jabber.org/streams" >

В ответ приходит начало XML потока сервера:
--S:
<?xml version='1.0'?>
<stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' id='2689330648' from='jabber.ru' version='1.0' xml:lang='en'>
<!-- И список поддерживаемых на данный момент действий -->
<stream:features>
<!-- Сервер поддерживает TLS шифрование потока (встроено в ядро Jabber протокола, но не обязательно, поддерживается большинством популярных серверов) Когда TLS шифрование включено, в Gajim около имени учетной записи отображается ключик -->
<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>
<!-- Сервер поддерживает сжатие потока -->
<compression xmlns='http://jabber.org/features/compress'>
<!-- Сжатие осуществляется gz библиотекой -->
<method>zlib</method>
</compression>
<!-- Перечисление механизмов SASL аутентификации -->
<mechanisms xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>
<!-- Поддерживается механизм DIGEST-MD5 -->
<mechanism>DIGEST-MD5</mechanism>
<!-- Поддерживается механизм PLAIN -->
<mechanism>PLAIN</mechanism>
</mechanisms>
<!-- Поддерживается регистрация на сервере -->
<register xmlns='http://jabber.org/features/iq-register'/>
</stream:features>


* This source code was highlighted with Source Code Highlighter
.

Аутентификация


Шифрование TLS включать не будем, не доросли еще))
Аутентификация в Jabber реализуется с помощью SASL (Simple Authentication and Security Layer). Из предложенных механизмов рекомендуется использовать механизм DIGEST-MD5, но он достаточно сложен в реализации, поэтому для начала рассмотрим механизм PLAIN.
Отсылается блок с атрибутом mechanism=«PLAIN» и содержащий закодированную base64 строку, в которую входит:
{идентификатор авторизации напр. user1@jabber.ru (не обязательно)}ASCII символ \x00{имя пользователя, напр. user1}ASCII символ \x00{пароль в открытом виде, напр 123456}
ASCII символ \x00 — это символ ASCII NUL с 16-ричным кодом 00
такую строку можно получить, например в PHP: base64_encode( $id."\x00".$login."\x00"$pass );
Можете попробовать здесь: Подготовка данных для SASL аутентификации
--C:
<auth xmlns="urn:ietf:params:xml:ns:xmpp-sasl" mechanism="PLAIN">dXNlcjFAamFiYmVyLnJ1AHVzZXIxADEyMzQ=</auth>

В ответ от сервера приходит сообщение об успешной авторизации (success)
--S:
<success xmlns='urn:ietf:params:xml:ns:xmpp-sasl'/>

ВНИМАНИЕ! НИКОГДА не используйте такой метод аутентификации (передача пароля и логина в открытом виде по незашифрованному каналу) в реальных сетевых программах. Можно так поступать, для небольшого повышения производительности, если клиент (бот) и Jabber сервер находятся в одной защищенной сети или на одном сервере. В настоящих сетевых Jabber приложениях либо используют TLS шифрование всего потока, либо метод аутентификации DIGEST-MD5, а чаще — и то и другое одновременно.

После успешной аутентификации необходимо начать новый XML поток
--C:
<stream:stream xmlns="jabber:client" to="jabber.ru" version="1.0" xmlns:stream="http://etherx.jabber.org/streams" >

Новый поток от сервера:
--S:
<?xml version='1.0'?>
<!-- Список доступных действий изменился -->
<stream:features>
<!-- Нужно привязать (bind) поток к определенному ресурсу -->
<bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'/>
<!-- Нужно начать XMPP-сессию -->
<session xmlns='urn:ietf:params:xml:ns:xmpp-session'/>
</stream:features>


Привязывание (bind) потока к ресурсу


iq — запросы к серверу. Используются для запроса/передачи какой-либо информации (ростер, «визитные карточки» и пр.) Имеют атрибуты type и id
type — один из списка: get (запрашивает данные), set (отправляет данные | устанавливает/заменяет какое-либо значение), result (ответ на успешное выполнение get или set, может содержать запрашиваемые данные), error (ответ на ошибочный запрос get или set)
id — просто каждый последующий iq запрос увеличивает id на единицу (инкрементируется). Сервер, отвечая на iq запрос повторяет его id. Это связано с тем, что иногда отсылается сразу несколько различных iq запросов.
В ответ на get или set запрос должен возвращаться ответ типа result (или error, если что-то не так)

Закрепляем этот поток за ресурсом «telnet»
--C:
<iq type="set" id="9746"><bind xmlns="urn:ietf:params:xml:ns:xmpp-bind">
<resource>telnet</resource>
</bind>
</iq>

В ответ приходит iq с типом result
--S:
<iq id='9746' type='result'>
<bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'>
<!-- полный JID имя@сервер/ресурс -->
<jid>user1@jabber.ru/telnet</jid>
</bind>
</iq>

Создание сессии


Затем сервер требовал создать сессию… Это нужно для отсылки статусов, сообщений да и вообще. Не будем отказывать:
--C:
<iq type="set" id="9747">
<session xmlns="urn:ietf:params:xml:ns:xmpp-session" />
</iq>

Сессия успешно создана:
--S:
<iq type='result' id='9747'>
<session xmlns='urn:ietf:params:xml:ns:xmpp-session'/>
</iq>

Рассылка статуса «доступен»


presence — для получения и рассылки широковещательной информации (напр. информация о статусе), а так же информации о подписке (например, добавление юзера в контакт-лист). Хотя может включать и указатель to для отсылки данных только определенному пользователю (напр. для определенного контакта ростера выставить статус «не беспокоить»:])
Может иметь атрибуты
from — JID имя@сервер/ресурс от кого presence. В принципе можно и не ставить — проставит сервер.
to — JID (с ресурсом или без) кому предназначен данный presence. Если не указано, рассылается всем, кто подписан (контакт-листу etc.)
type — тип presence, если отсутствует — то простое сообщение о статусе, иначе один из списка: unavailable (отключен),probe (запрос статуса клиента с сервера), subscribe (запрос подписки/просьба добавить в ростер), unsubscribed (отозвать подписку/удалить из ростера), subscribed (одобрить подписку/добавить в ростер)

Отсылаем сообщение об изменении статуса:
--C:
<presence>
<!-- Выставляем приоритет для этого ресурса (сообщения приходят ресурсу с высшим из подключенных или сразу всем, если приоритеты одинаковые) -->
<priority>50</priority>
<!-- Стандартный статус, если отсутствует — то «Доступен» либо один из списка xa (eXtended Away недоступен), away (отошел), chat (готов поболтать), dnd (занят — не беспокоить) -->
<show>chat</show>
<!-- Капс, что-то вроде идентификатора софта клиента -->
<c xmlns="http://jabber.org/protocol/caps" node="http://gajim.org/caps" ext="xhtml cstates" ver="0.11.4" />
<!-- Расширенный текстовый статус -->
<status>Ааа ппц я в консоли</status>
<!-- Обновленная картинка x-статуса (ее sha1-хеш) -->
<x xmlns="vcard-temp:x:update">
<photo>b2730e40aba4f7225456d0b4789bf2d5af34c3e3</photo>
</x>
</presence>


Хочу заметить, что ни один из вложенных параметров не обязателен. Можно отправить один <priority></priority> или только <status></status>… Что хотим изменить то и отсылаем.

В ответ сервер рассылает всем из твоего контакт-листа (точнее тем, кто подписался командой prescence type=«subscribe» в т.ч. иногда и тебе) сообщение
--S:
<presence from='seriy.pr@jabber.ru/telnet' to='seriy.pr@jabber.ru/telnet'>
<priority>50</priority>
<show>chat</show>
<c xmlns="http://jabber.org/protocol/caps" node="http://gajim.org/caps" ext="xhtml cstates" ver="0.11.4" />
<status>Ааа ппц я в консоли</status>
<x xmlns="vcard-temp:x:update">
<photo>b2730e40aba4f7225456d0b4789bf2d5af34c3e3</photo>
</x>
</presence>

Отправка/получение сообщений


message-для отсылки и получения сообщений. Имеет атрибуты
to адресат — JID (с ресурсом или без)
from отправитель — JID с ресурсом (чужой подставить не получится:P)
type тип сообщения — один из списка: chat — сообщение тет-а-тет чата (как ICQ, обычно используется по-умолчанию), normal — одиночное сообщение, откроется в отдельном окошке, на него можно ответить таким-же сообщением, groupchat (сообщение в конференции), headline (сообщения, отсылаемые автоматическими оповещалками, RSS-ботами и т.п., откроется в отдельном окошке, на него нельзя ответить), error — сообщение об ошибке, отображается в окне чата в особым образом отформатированном виде
Отправим чат-сообщение автору статьи:
--C:
<message to="seriy.pr@jabber.ru" from="user1@jabber.ru/telnet" type="chat">
<!-- Собственно, само тело сообщения -->
<body>Тук-тук!</body>
<!-- Тут может содержаться информация о том, что автор сообщения «печатает текст»/«прекратил печатать»/«закрыл окошко» и т.п., это вам на домашнее задание -->
</message>
(попугайте друзей — отправьте сообщение с типом error)

Рассылка статуса «отключен»


Длч правильного завершения сеанса обычно рассылается статусное сообщение «отключен» (type=«unavailable»)
--C:
<presence xmlns="jabber:client" type="unavailable" />

Закрытие потока


Закрываем поток такой командой
--C:
</stream:stream>

И сервер тоже закрывает поток
--S:
</stream:stream>

Отключение от сервера


Соединение будет закрыто сразу после закрытия потока по инициативе сервера.

Вот в общем-то и все.

В продолжении хотелось-бы рассмотреть:
  • Аутентификация методом DIGEST-MD5 (если сам разберусь)
  • Включение шифрования канала (тут изменения будут небольшие)
  • Работа с ростером
  • Работа с конференциями
  • Работа с «визитной карточкой»
  • Обзор сервисов
  • Работа с транспортами (м/б)

Литература:


Кросспост с моего блога
http://tools.ietf.org/html/rfc3920 rfc3920 XMPP ядро протокола
http://tools.ietf.org/html/rfc3921 rfc3921 XMPP статусы, сессии и т.п.
http://tools.ietf.org/html/rfc2831 rfc2831 SASL аутентификация
http://www.bog.pp.ru/work/SASL.html немного о SASL по-русски
http://ru.wikipedia.org/wiki/XMPP XMPP в вики
http://ru.wikipedia.org/wiki/Jabber Jabber в вики
Теги:jabberxmppxmlпротокол
Хабы: Сетевые технологии
Всего голосов 79: ↑75 и ↓4+71
Просмотры3.6K
Комментарии Комментарии 21

Похожие публикации

Лучшие публикации за сутки