Блог - Linux, программирование, Я!

WEB, ИнтернетПротоколы прикладного уровня: 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>

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

Шифрование 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 в вики

  1. vlad
    2009-03-31 18:57:09 | #

    Это можно оправлять/получать, импользуя ms-клинет telnet?!

  2. 2009-04-08 17:36:38 | #

    честно говоря, не пробовал… Но теоретически можно.

  3. 2009-11-10 14:06:15 | #

    Спасибо некоторые моменты полезны

  4. 000
    2010-06-19 20:52:06 | #

    Спасибо, пригодилось. Жаль, нет продолжения.

    • 2010-06-19 21:19:27 | #

      Спасибо.

      Рад что пригодилось! В принципе по ссылкам на спецификацию можно остальное самостоятельно разобрать. А продолжения нет, т.к. для меня неактуально стало.

      • 000
        2010-06-21 13:21:19 | #

        Не могли бы Вы подсказать, где можно почитать, желательно по-русски, о запросах при общении в конференциях jabber?

        • 000
          2010-06-21 16:25:30 | #

          Отвечу сам на свой вопрос):

          http://xmpp.org/extensions/xep-0045.html – суровая база. Мне лично разобраться было трудно.

          http://commons.oreilly.com/wiki/index.php/JabChapter_9 – Тут попонятней. Хотя и намного короче.

          • 2010-06-21 20:45:05 | #

            Спасибо за дополнения.

            На русском языке я почти не смог найти информации по XMPP, все на английском.

            Вообще, почти все сообщения в Jabber – это XML теги <presence/> <message/> и <iq/>, главное – разобраться в их атрибутах и порядке, в котором они посылаются)))