Джаббер чат на веб-странице

    Прочитав пост на хабре про онлайн чат для сайта через джаббер, мне стало интересно — а как оно работает и как такое можно сделать самому, без готовых приложений. В итоге у меня получилась очень простая заготовка «чата для сайта через джаббер». К сожалению у меня нет выделенного сервера с линуксом для тестов, поэтому был использован локальный компьютер с Win7 (и сервером Apache).

    Как это вообще должно работать: пользователь заходит на сайт, и видит окошко, куда можно разговаривать. После того как пользователь послал сообщение, оно прилетает на указанный джаббер аккаунт. Получатель этого сообщения может написать ответ и оно придёт посетителю сайта.
    Что для этого нужно:
    • Jabber сервер, можно публичный, можно локальный. Я выбрал Openfire и установил его локально. Сервер должен поддерживать Bosh — XEP-0124: Bidirectional-streams Over Synchronous HTTP, об этом чуть позже.
    • JS библиотека, которая будет реализовывать джаббер-клиент на сайте. Я взял Strophe. Это достаточно низкоуровневая библиотека, в которой нет функций типа «ПослатьСообщение(Куда, Текст)». Для достижения нужных действий нужно вручную составлять команды джаббер серверу (в XML). Удобные средства для создания XML в Strophe есть :)

    BOSH


    JS не умеет создавать TCP соединения с другим сервером/клиентом, что необходимо для реализации джаббер-клиента. JS может посылать только HTTP запросы. Поэтому нужен специальный механизм, который позволит работать с TCP соединениями посредством HTTP. Это и есть BOSH.

    По-умолчанию BOSH в Openfire включён и имеет адрес localhost:7070/http-bind. Но если указать этот адрес при соединении, ничего не выходит. Проблема хорошо описана здесь, что бы заработало нужно написать редирект для апача и раскомментировать модули proxy_module и proxy_http_module:

    httpd.conf:
    ProxyRequests Off
    ProxyPass /http-bind http://127.0.0.1:7070/http-bind/
    ProxyPassReverse /http-bind http://127.0.0.1:7070/http-bind/

    Чат на сайте


    Итак, всё очень просто, устанавливаем Openfire, создаём пользователя, например, site, и создаём тестовую страницу (в качестве основы я брал строфовский пример echobot). На странице есть поле, куда пользователь пишет своё сообщение, и поле с историей чата.

    Пишем обработчик загрузки страницы, который будет логинится на джаббер сервер:
    $(function () {
        connection = new Strophe.Connection('/http-bind');
        connection.connect('site@r1c', 'site', onConnect);
    });

    В обработчике onConnect мы добавляем «слушателя» на события, связанные с приходом сообщений (в случае успешного логина):
    function onConnect(status){
        if (status == Strophe.Status.CONNECTING) {
            log('Strophe is connecting.');
        }
        else if (status == Strophe.Status.CONNFAIL) {
            log('Strophe failed to connect.');
        }
        else if (status == Strophe.Status.DISCONNECTING) {
            log('Strophe is disconnecting.');
        }
        else if (status == Strophe.Status.DISCONNECTED) {
            log('Strophe is disconnected.');
        }
        else if (status == Strophe.Status.CONNECTED) {
            log('Strophe is connected, ' + connection.jid);
            
            connection.addHandler(onMessage, null, 'message', null, null, null);
            connection.send($pres().tree());
        }
    }

    Обработчику onMessage приходят XML данные. Разбираем их что бы узнать текст сообщения, кто отправитель и т.д.:
    function onMessage(msg) {
        var to = msg.getAttribute('to');
        var from = msg.getAttribute('from');
        var type = msg.getAttribute('type');
        var elems = msg.getElementsByTagName('body');
    
        if (type == "chat" && elems.length > 0) {
            var body = elems[0];
        
            AddText(Strophe.getText(body), 'in');
        }
    
        // we must return true to keep the handler alive.  
        // returning false would remove it after it finishes.
        return true;
    }

    Обработчик кнопки «Отправить» формирует XML с необходимыми данными и просит Strophe отправить их. В качестве принимающего используется пользователь admin:
    function Send(message) {
        var msg = $msg({to: 'admin@r1c', from: connection.jid, type: 'chat'}).c('body').t(document.URL + '\n' + message);
        connection.send(msg.tree());
        
        AddText(message, 'out');
        $('input#message').val('');
    }

    Итог



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

    Можно придумать ещё очень много усовершенствований. Например:
    • Нормальный интерфейс чата на сайте :)
    • Автоматическая регистрация новых пользователей на джаббер-сервере, если в этом есть необходимость. Для этого можно использовать In-band registration — регистрация через XML команды джаббера, а не через веб-интерфейс.
    • Сохранение состояния чата при переходе между разными страницами сайта
    • Сохранение истории чата на сервере
    • и т.д.
    Share post

    Comments 19

      +1
      Огромное спасибо как раз искал инфу чтобы начать дальше разбираться с Jabber-ом
        –1
        Когда ужé кто-нибудь допилит до уровня $('textarea:first').Jabber('servername.example.org');
          +1
          Ну подобные штуки есть, готовые приложения, как в том посте на который я давал ссылку вначале. А здесь я специально разбирался как это делается внтури.
          –3
          Велосипед, ибо hab.la olark, на который ссылка в начале.
            +2
            Он самый. "… мне стало интересно — а как оно работает и как такое можно сделать самому, без готовых приложений"
              +1
              20 разговоров — это несерьёзно.
              +1
              А я пользуюсь гугловым: www.google.com/talk/service/badge/New
              есть конечно некоторые минусы — вроде того, что не видно IP/Browser клиента, но по удобству пока более ничего вменяемого не нашел
                0
                Я боюсь спросить… пароль от JIT аккаунта прямо в Javascript-e указан?
                  0
                  Ога :) Но если это будет свой локальный Jabber-сервак, там можно настроить ограничение на доступ по IP. Ещё можно использовать анонимный логин.
                    0
                    Упс, глупость сказал про ip…
                  0
                  Интересная статья =)
                    0
                    race1 молодец! Заношу в кладезь знаний до светлого будущего :)
                      +4
                      В клавогонках именно так и сделано. Только я выбрал ejabberd, а не openfire, ибо слышал много отзывов о прожорливости последнего; и плюс там используется расширение Multi User Chat (XEP-0045) с отдельной комнатой для каждой игры. Кому интересно, можете посмотреть работающий пример прямо тут: klavogonki.ru/gamelist/
                        0
                        И еще поправлю, что вовсе необязательно ограничиваться серверами, поддерживающими BOSH. У автора Strophe есть также замечательная штука Punjab, с помощью нее можно общаться через BOSH теоретически с любым сервером.
                          +1
                          Также рекомендую связку ejabberd + JsJaC (сейчас работает в Виртономике). Из плюсов: не нужна громозкая Java, а в JsJac есть хороший пример.

                          Если интересует полноценный JS клиент, то смотрите jwchat.
                            –1
                            А какой смысл в этом чате? Почему мне может понадобиться использовать джаббер?
                              0
                              Связь с техподдержкой, например. Предпродажные вопросы. В штатах уже давно практикуется.
                              0
                              Рекомендую ещё почитать про Comet вообще, там много полезного. Например можно сделать потоковый вывод из PHP в Javascript/Flex, буквально вчера это делал.
                                0
                                Один пользователь для кучи посетителей — это не серьезно.
                                Автоматическая регистрация новых пользователей, плюс запоминание старых по сессии.

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