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

    Similar posts

    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More
    Ads

    Comments 21

      –2
      Спасибо, очень актуально…
        +4
        не знаю, как на jabber.ru, но вообще сервер ejabberd (у меня стоит он) ругается на ваш xmlns:stream в самом первом заголовке потока. Если указать namespace как он сам уазывает — с http — всё работает:

        И Kopete шлёт такой же точно xmlns — с http.

        <stream:stream xmlns="jabber:client" to="jabber.rterm.ru" version="1.0" xmlns:stream="http://etherx.jabber.org/streams">


        И ещё, в следующий раз, когда будете xml постить, сделайте так, чтобы вокруд значений параметров двойные кавычки не превращались в ёлочки. Они там не в тему :)
          +1
          Видимо злобный парсер сгрыз хттп:// (а он там был)
          Проставил везде тег code — вроде нормально стало. Кавычки поправлю.
          0
          Вот спасибо! как раз хотел попробовать реализовать собственную jabber-либу и бота для конференции до кучи
            0
            Свою либу писать вовсе не обязательно, т.к. их и так куча xmpp.org/software/libraries.shtml а вот бота — это да, это запросто.
              0
              минимальный обвес для бота я уже реализовал на jabber-net для C#, просто интересно разобраться как оно все-таки работает унутре
            0
            Если бы кто-то такой же мануал и для http-bind написал…
            –5
            [trolling][irony]XML это круто, необычайно круто. Особенно для сетевого протокола поверх HTTP.[/irony]

            Автор, Вы не заметили, что Ваша статья чуть менее чем полностью состоит из XML? :)[/trolling]

            PS а вообще жаль. этот xmpp сильно over-engineered.
              0
              Насчет over-engineered согласен, но это сделано ээ… для гибкости и расширяемости… А она, в принципе, на практике часто применяется (транспорты те-же)
              В принципе, включение сжатия трафика частично поможет решить потенциальные проблемы.
                0
                > Насчет over-engineered согласен, но это сделано ээ… для гибкости и расширяемости…

                4.2. Расширяемость бывает разная. XML расширяем только на уровне вкладывания узлов в другие узлы, а обработку этого расширенного богатства все равно придется делать в программе.

                > В принципе, включение сжатия трафика частично поможет решить потенциальные проблемы.

                На спичках экономим?

                XML = Extensible Markup Language. Особое внимание прошу обратить на слово markup. Нет, конечно, можно шурупы забивать молотком, но нафига?

                OTOH даже если и пользоваться XML не по назначению («а чо, так ведь все районные сантехники делают»), то все равно вопрос: нафига?

                -К.О.

                PS ох, школята опять минусуют
                –1
                Зря вы про over-engineered. Сравните с SOAP — полюбите Jabber за простоту и логичность.
                Возможно вы имели ввиду излишние обьемы трафика — ну так это решается банальным сжатием (правда и клиент и сервер должны его поддерживать)
              • UFO just landed and posted this here
                  +2
                  +1 :)
                  Я всегда думал что SMTP/POP3 с телнета — извращение.
                  Но автор развеял мои комплексы по этому поводу :)
                  –1
                  Большое спасибо. Буду ждать продолжения.
                    0
                    Кстати, пользуясь случаем хотел спросить насчет DIGEST-MD5. Может кто-нибудь проконсультировать?
                    Не нашел толкового описания как вычисляется response и rspaut tools.ietf.org/html/rfc3920#section-6.5
                    В препарированной мной для написания статьи библиотеке не было поддержки этого метода…
                    Конечно, в любом случае разберусь, но может у кого есть доп. информация?
                      0
                        0
                        P.S. rspauth там же в п.2.1.3
                          0
                          Спасибо, но это то-же, что и tools.ietf.org/html/rfc2831#section-2.1.3 (который указан в списке «литературы»)
                          В него я сходу не въехал к сожалению)

                          С response уже разобрался, с rspauth продолжаю втыкать…
                        0
                        Все, вопрос DIGEST-MD5 закрыт…
                        +8
                        Серия постов для сотрудников mail.ru? :-)

                        Only users with full accounts can post comments. Log in, please.