XMPP即时通讯协议使用(七)——利用Strophe实现WebIM及strophe.plugins插件使用

发布时间: 5年前 (2020-05-04)浏览: 1168评论: 0

Strophe简介与Openfire配置


Strophe.js是为XMPP写的一个js类库。因为http协议本身不能实现持久连接,所以strophe利用BOSH模拟实现持久连接。

官方文档:

http://strophe.im/strophejs/doc/1.2.15/files/strophe-js.html

https://stackoverflow.com/questions/17311901/strophe-js-giving-authfail-status-always

Strophe操作相关插件:

https://github.com/ggozad/strophe.plugins/

https://github.com/gm19900510/strophejs-plugins


原文链接: https://www.cnblogs.com/gmhappy/p/9472406.html

连接状态常量
发起一个链接后,会返回一个连接状态
Status.ERROR
错误
Status.CONNECTING
正在创建连接
Status.CONNFAIL
连接创建失败
Status.AUTHENTICATING
正在验证
Status.AUTHFAIL
验证失败
Status.CONNECTED
连接创建成功
Status.DISCONNECTED
连接已关闭
Status.DISCONNECTING
连接正在关闭
XMPP服务器通常会实现BOSH扩展,下面是Openfire和Tigase的BOSH默认URL:

Openfire:http://host:7070/http-bind
Tigase:http://host:5280

在使用Strophe.js的时候,需要使用对应的HTTP地址才能连接上XMPP服务器。

如果使用Opnefire,还需要在管理后台配置一下:

注意图中的pc-20170308pkrs是下图的服务器名称


XMPP协议简介:

XMPP服务器和客户端之间,是通过XML节(XML Stanza)来进行通讯。其中有三种非常重要的XML Stanza类型:<message>、<presence>、<iq>。

<message>:

聊天消息的发送和接收就是通过message节来实现。例如xxg1@host发送一条信息"你好"给xxg2@host,xxg1@host客户端会将下面的这段XML发送到XMPP服务器,服务器再推送给xxg2@host客户端。其中<message>的from属性是发送者,to属性是接收者,<body>子元素的内容就是聊天信息。

<message from="a@pc-20170308pkrs" to="b@pc-20170308pkrs" type="chat">  
    <body>你好</body>  </message>
<presence>:
可用于表明用户的状态,例如用户状态改变成“Do not disturb”(“请勿打扰”),会向服务器发送:
<presence from="xxg@host">  
    <status>Do not disturb</status>  
    <priority>0</priority>  
    <show>dnd</show>  </presence>
<iq>:

iq即Info/Query,采用“请求-响应”机制,类似于HTTP的机制。下面的例子是客户端通过<iq>请求获取联系人,XMPP服务器将结果返回:

客户端请求获取联系人:
<iq from='a@pc-20170308pkrs' id='bv1bs71f' type='get'>  
    <query xmlns='jabber:iq:roster'/>  </iq>
服务器结果返回:
  
<iq to='a@pc-20170308pkrs' id='bv1bs71f' type='result'>  
    <query xmlns='jabber:iq:roster'>  
        <item jid='b@pc-20170308pkrs'/>  
        <item jid='c@pc-20170308pkrs'/>  
    </query>  </iq>

构建WebIM

新建echobot.html

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml"><head>
  <title>Strophe.js Echobot Example</title>
  <script type='text/javascript'
          src='jquery-1.9.1.min.js'></script>
  <script type='text/javascript'
          src='strophe.min.js'></script>
  <script type='text/javascript'
          src='echobot.js'></script></head><body>
    JID:<input type="text" id="input-jid" value="gm@pc-20170308pkrs">
    <br>
    密码:<input type="password" id="input-pwd">
    <br>
    <button id="btn-login">登录</button>
    <div id="msg" style="height: 400px; width: 400px; overflow: scroll;"></div>
    联系人JID:    <input type="text" id="input-contacts">
    <br>
    消息:    <br>
    <textarea id="input-msg" cols="30" rows="4"></textarea>
    <br>
    <button id="btn-send">发送</button></body></html>

新建echobot.js

var BOSH_SERVICE = 'http://pc-20170308pkrs:7070/http-bind/';
// XMPP连接
var connection = null;


// 当前状态是否连接
var connected = false;


// 当前登录的JID
var jid = "";


// 连接状态改变的事件
function onConnect(status) {
    console.log('status: ' + status)
    if (status == Strophe.Status.CONNFAIL) {
        alert("连接失败!");
    } else if (status == Strophe.Status.AUTHFAIL) {
        alert("登录失败!");
    } else if (status == Strophe.Status.DISCONNECTED) {
        alert("连接断开!");
        connected = false;
    } else if (status == Strophe.Status.CONNECTED) {
        alert("连接成功,可以开始聊天了!");		console.log('pubsub',connection)
        connected = true;


        // 当接收到<message>节,调用onMessage回调函数
        connection.addHandler(onMessage, null, null, null, null, null);


        // 首先要发送一个<presence>给服务器(initial presence)
        connection.send($pres().tree());			//获取订阅的主题信息	connection.pubsub.getSubscriptions(onMessage,5000);		    }
}






// 接收到<message>function onMessage(msg) {	    console.log('--- msg ---', msg);


    // 解析出<message>的from、type属性,以及body子元素
    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];
        $("#msg").append(from + ":<br>" + Strophe.getText(body) + "<br>")
    }
    return true;
}


$(document).ready(function() {


    // 通过BOSH连接XMPP服务器
    $('#btn-login').click(function() {
        if(!connected) {
            console.log('jid: ' + $("#input-jid").val());
            console.log('pwd: ' + $("#input-pwd").val());
            connection = new Strophe.Connection(BOSH_SERVICE);
            connection.connect($("#input-jid").val(), $("#input-pwd").val(), onConnect);
            jid = $("#input-jid").val();
        }
    });


    // 发送消息
    $("#btn-send").click(function() {
        if(connected) {
            if($("#input-contacts").val() == '') {
                alert("请输入联系人!");
                return;
            }


            // 创建一个<message>元素并发送
            var msg = $msg({
                to: $("#input-contacts").val(),
                from: jid, 
                type: 'chat'
            }).c("body", null, $("#input-msg").val());
            connection.send(msg.tree());


            $("#msg").append(jid + ":<br>" + $("#input-msg").val() + "<br>");
            $("#input-msg").val('');
        } else {
            alert("请先登录!");
        }
    });
});

strophe.plugins插件使用(connection+“插件名称”+ “对应方法”)

connection.pubsub.getSubscriptions(onMessage,5000);

新建strophe.pubsub.js

/*
    This program is distributed under the terms of the MIT license.
    Please see the LICENSE file for details.
    Copyright 2008, Stanziq  Inc.
    Overhauled in October 2009 by Liam Breck [How does this affect copyright?]
*/

/** File: strophe.pubsub.js
 *  A Strophe plugin for XMPP Publish-Subscribe.
 *
 *  Provides Strophe.Connection.pubsub object,
 *  parially implementing XEP 0060.
 *
 *  Strophe.Builder.prototype methods should probably move to strophe.js
 */

/** Function: Strophe.Builder.form
 *  Add an options form child element.
 *
 *  Does not change the current element.
 *
 *  Parameters:
 *    (String) ns - form namespace.
 *    (Object) options - form properties.
 *
 *  Returns:
 *    The Strophe.Builder object.
 */
Strophe.Builder.prototype.form = function (ns, options)
{
    var aX = this.node.appendChild(Strophe.xmlElement('x', {"xmlns": "jabber:x:data", "type": "submit"}));
    aX.appendChild(Strophe.xmlElement('field', {"var":"FORM_TYPE", "type": "hidden"}))
      .appendChild(Strophe.xmlElement('value'))
      .appendChild(Strophe.xmlTextNode(ns));

    for (var i in options) {
        aX.appendChild(Strophe.xmlElement('field', {"var": i}))
        .appendChild(Strophe.xmlElement('value'))
        .appendChild(Strophe.xmlTextNode(options[i]));
    }
    return this;
};

/** Function: Strophe.Builder.list
 *  Add many child elements.
 *
 *  Does not change the current element.
 *
 *  Parameters:
 *    (String) tag - tag name for children.
 *    (Array) array - list of objects with format:
 *          { attrs: { [string]:[string], ... }, // attributes of each tag element
 *             data: [string | XML_element] }    // contents of each tag element
 *
 *  Returns:
 *    The Strophe.Builder object.
 */
Strophe.Builder.prototype.list = function (tag, array)
{
    for (var i=0; i < array.length; ++i) {        
        this.c(tag, array[i].attrs)        
        this.node.appendChild(array[i].data.cloneNode ? array[i].data.cloneNode(true) : Strophe.xmlTextNode(array[i].data));        
        this.up();
    }    
    return this;
};
Strophe.Builder.prototype.children = function (object) {    
    var key, value;    
    for (key in object) {        
        if (!object.hasOwnProperty(key)) 
            continue;        
        value = object[key];
      if (Array.isArray(value)) {            
          this.list(key, value);
     } else if (typeof value === 'string') {            
         this.c(key, {}, value);
        
     } else if (typeof value === 'number') {            
         this.c(key, {}, ""+value);
     } else if (typeof value === 'object') {            
         this.c(key).children(value).up();
     } else {            
         this.c(key).up();
     }
  }    
  return this;
};

// TODO Ideas Adding possible conf values?
/* Extend Strophe.Connection to have member 'pubsub'.
 */Strophe.addConnectionPlugin('pubsub', {
/*Extend connection object to have plugin name 'pubsub'.*/    _connection: null,    _autoService: true,    service: null,    jid: null,

    //The plugin must have the init function.
    init: function(conn) {        this._connection = conn;

        /*        Function used to setup plugin.
        */

        /* extend name space
        *  NS.PUBSUB - XMPP Publish Subscribe namespace
        *              from XEP 60.
        *
        *  NS.PUBSUB_SUBSCRIBE_OPTIONS - XMPP pubsub
        *                                options namespace from XEP 60.
        */        Strophe.addNamespace('PUBSUB',"http://jabber.org/protocol/pubsub");        Strophe.addNamespace('PUBSUB_SUBSCRIBE_OPTIONS',                             Strophe.NS.PUBSUB+"#subscribe_options");        Strophe.addNamespace('PUBSUB_ERRORS',Strophe.NS.PUBSUB+"#errors");        Strophe.addNamespace('PUBSUB_EVENT',Strophe.NS.PUBSUB+"#event");        Strophe.addNamespace('PUBSUB_OWNER',Strophe.NS.PUBSUB+"#owner");        Strophe.addNamespace('PUBSUB_AUTO_CREATE',                             Strophe.NS.PUBSUB+"#auto-create");        Strophe.addNamespace('PUBSUB_PUBLISH_OPTIONS',                             Strophe.NS.PUBSUB+"#publish-options");        Strophe.addNamespace('PUBSUB_NODE_CONFIG',                             Strophe.NS.PUBSUB+"#node_config");        Strophe.addNamespace('PUBSUB_CREATE_AND_CONFIGURE',                             Strophe.NS.PUBSUB+"#create-and-configure");        Strophe.addNamespace('PUBSUB_SUBSCRIBE_AUTHORIZATION',                             Strophe.NS.PUBSUB+"#subscribe_authorization");        Strophe.addNamespace('PUBSUB_GET_PENDING',                             Strophe.NS.PUBSUB+"#get-pending");        Strophe.addNamespace('PUBSUB_MANAGE_SUBSCRIPTIONS',                             Strophe.NS.PUBSUB+"#manage-subscriptions");        Strophe.addNamespace('PUBSUB_META_DATA',                             Strophe.NS.PUBSUB+"#meta-data");        Strophe.addNamespace('ATOM', "http://www.w3.org/2005/Atom");        if (conn.disco)            conn.disco.addFeature(Strophe.NS.PUBSUB);

    },

    // Called by Strophe on connection event
    statusChanged: function (status, condition) {        var that = this._connection;
        if (this._autoService && status === Strophe.Status.CONNECTED) {            this.service =  'pubsub.'+Strophe.getDomainFromJid(that.jid);            this.jid = that.jid;
        }
    },

    /***Function
    Parameters:
    (String) jid - The node owner's jid.
    (String) service - The name of the pubsub service.
    */    connect: function (jid, service) {        var that = this._connection;
        if (service === undefined) {            service = jid;
            jid = undefined;
        }        this.jid = jid || that.jid;        this.service = service || null;        this._autoService = false;
    },

    /***Function
    Create a pubsub node on the given service with the given node
    name.
    Parameters:
    (String) node -  The name of the pubsub node.
    (Dictionary) options -  The configuration options for the  node.
    (Function) call_back - Used to determine if node
    creation was sucessful.
    Returns:
    Iq id used to send subscription.
    */    createNode: function(node,options, call_back) {        var that = this._connection;

        var iqid = that.getUniqueId("pubsubcreatenode");        var iq = $iq({from:this.jid, to:this.service, type:'set', id:iqid})          .c('pubsub', {xmlns:Strophe.NS.PUBSUB})          .c('create',{node:node});        if(options) {            iq.up().c('configure').form(Strophe.NS.PUBSUB_NODE_CONFIG, options);
        }        that.addHandler(call_back, null, 'iq', null, iqid, null);        that.send(iq.tree());        return iqid;
    },

    /** Function: deleteNode
     *  Delete a pubsub node.
     *
     *  Parameters:
     *    (String) node -  The name of the pubsub node.
     *    (Function) call_back - Called on server response.
     *
     *  Returns:
     *    Iq id
     */    deleteNode: function(node, call_back) {        var that = this._connection;
        var iqid = that.getUniqueId("pubsubdeletenode");        var iq = $iq({from:this.jid, to:this.service, type:'set', id:iqid})          .c('pubsub', {xmlns:Strophe.NS.PUBSUB_OWNER})          .c('delete', {node:node});        that.addHandler(call_back, null, 'iq', null, iqid, null);        that.send(iq.tree());        return iqid;
    },

    /** Function
     *
     * Get all nodes that currently exist.
     *
     * Parameters:
     *   (Function) success - Used to determine if node creation was sucessful.
     *   (Function) error - Used to determine if node
     * creation had errors.
     */    discoverNodes: function(success, error, timeout) {

        //ask for all nodes
        var iq = $iq({from:this.jid, to:this.service, type:'get'})          .c('query', { xmlns:Strophe.NS.DISCO_ITEMS });        return this._connection.sendIQ(iq.tree(),success, error, timeout);
    },

    /** Function: getConfig
     *  Get node configuration form.
     *
     *  Parameters:
     *    (String) node -  The name of the pubsub node.
     *    (Function) call_back - Receives config form.
     *
     *  Returns:
     *    Iq id
     */    getConfig: function (node, call_back) {        var that = this._connection;
        var iqid = that.getUniqueId("pubsubconfigurenode");        var iq = $iq({from:this.jid, to:this.service, type:'get', id:iqid})          .c('pubsub', {xmlns:Strophe.NS.PUBSUB_OWNER})          .c('configure', {node:node});        that.addHandler(call_back, null, 'iq', null, iqid, null);        that.send(iq.tree());        return iqid;
    },

    /**
     *  Parameters:
     *    (Function) call_back - Receives subscriptions.
     *
     *  http://xmpp.org/extensions/tmp/xep-0060-1.13.html
     *  8.3 Request Default Node Configuration Options
     *
     *  Returns:
     *    Iq id
     */    getDefaultNodeConfig: function(call_back) {        var that = this._connection;
        var iqid = that.getUniqueId("pubsubdefaultnodeconfig");        var iq = $iq({from:this.jid, to:this.service, type:'get', id:iqid})          .c('pubsub', {'xmlns':Strophe.NS.PUBSUB_OWNER})          .c('default');        that.addHandler(call_back, null, 'iq', null, iqid, null);        that.send(iq.tree());        return iqid;
    },

    /***Function
        Subscribe to a node in order to receive event items.
        Parameters:
        (String) node         - The name of the pubsub node.
        (Array) options       - The configuration options for the  node.
        (Function) event_cb   - Used to recieve subscription events.
        (Function) success    - callback function for successful node creation.
        (Function) error      - error callback function.
        (Boolean) barejid     - use barejid creation was sucessful.
        Returns:
        Iq id used to send subscription.
    */    subscribe: function(node, options, event_cb, success, error, barejid) {        var that = this._connection;
        var iqid = that.getUniqueId("subscribenode");        var jid = this.jid;
        if(barejid)            jid = Strophe.getBareJidFromJid(jid);

        var iq = $iq({from:this.jid, to:this.service, type:'set', id:iqid})          .c('pubsub', { xmlns:Strophe.NS.PUBSUB })          .c('subscribe', {'node':node, 'jid':jid});        if(options) {            iq.up().c('options').form(Strophe.NS.PUBSUB_SUBSCRIBE_OPTIONS, options);
        }

        //add the event handler to receive items
        that.addHandler(event_cb, null, 'message', null, null, null);        that.sendIQ(iq.tree(), success, error);        return iqid;
    },

    /***Function
        Unsubscribe from a node.
        Parameters:
        (String) node       - The name of the pubsub node.
        (Function) success  - callback function for successful node creation.
        (Function) error    - error callback function.
    */    unsubscribe: function(node, jid, subid, success, error) {        var that = this._connection;
        var iqid = that.getUniqueId("pubsubunsubscribenode");        var iq = $iq({from:this.jid, to:this.service, type:'set', id:iqid})          .c('pubsub', { xmlns:Strophe.NS.PUBSUB })          .c('unsubscribe', {'node':node, 'jid':jid});        if (subid) iq.attrs({subid:subid});        that.sendIQ(iq.tree(), success, error);        return iqid;
    },

    /***Function
    Publish and item to the given pubsub node.
    Parameters:
    (String) node -  The name of the pubsub node.
    (Array) items -  The list of items to be published.
    (Function) call_back - Used to determine if node
    creation was sucessful.
    */    publish: function(node, items, call_back) {        var that = this._connection;
        var iqid = that.getUniqueId("pubsubpublishnode");        var iq = $iq({from:this.jid, to:this.service, type:'set', id:iqid})          .c('pubsub', { xmlns:Strophe.NS.PUBSUB })          .c('publish', { node:node, jid:this.jid })          .list('item', items);        that.addHandler(call_back, null, 'iq', null, iqid, null);        that.send(iq.tree());        return iqid;
    },

    /*Function: items
    Used to retrieve the persistent items from the pubsub node.
    */    items: function(node, success, error, timeout) {
        //ask for all items
        var iq = $iq({from:this.jid, to:this.service, type:'get'})          .c('pubsub', { xmlns:Strophe.NS.PUBSUB })          .c('items', {node:node});        return this._connection.sendIQ(iq.tree(), success, error, timeout);
    },

    /** Function: getSubscriptions
     *  Get subscriptions of a JID.
     *
     *  Parameters:
     *    (Function) call_back - Receives subscriptions.
     *
     *  http://xmpp.org/extensions/tmp/xep-0060-1.13.html
     *  5.6 Retrieve Subscriptions
     *
     *  Returns:
     *    Iq id
     */    getSubscriptions: function(call_back, timeout) {        var that = this._connection;
        var iqid = that.getUniqueId("pubsubsubscriptions");        var iq = $iq({from:this.jid, to:this.service, type:'get', id:iqid})          .c('pubsub', {'xmlns':Strophe.NS.PUBSUB})          .c('subscriptions');        that.addHandler(call_back, null, 'iq', null, iqid, null);        that.send(iq.tree());		console.log('-- iq pubsub --',iq.tree())        return iqid;
    },

    /** Function: getNodeSubscriptions
     *  Get node subscriptions of a JID.
     *
     *  Parameters:
     *    (Function) call_back - Receives subscriptions.
     *
     *  http://xmpp.org/extensions/tmp/xep-0060-1.13.html
     *  5.6 Retrieve Subscriptions
     *
     *  Returns:
     *    Iq id
     */    getNodeSubscriptions: function(node, call_back) {        var that = this._connection;
       var iqid = that.getUniqueId("pubsubsubscriptions");       var iq = $iq({from:this.jid, to:this.service, type:'get', id:iqid})         .c('pubsub', {'xmlns':Strophe.NS.PUBSUB_OWNER})         .c('subscriptions', {'node':node});       that.addHandler(call_back, null, 'iq', null, iqid, null);       that.send(iq.tree());       return iqid;
    },

    /** Function: getSubOptions
     *  Get subscription options form.
     *
     *  Parameters:
     *    (String) node -  The name of the pubsub node.
     *    (String) subid - The subscription id (optional).
     *    (Function) call_back - Receives options form.
     *
     *  Returns:
     *    Iq id
     */    getSubOptions: function(node, subid, call_back) {        var that = this._connection;
        var iqid = that.getUniqueId("pubsubsuboptions");        var iq = $iq({from:this.jid, to:this.service, type:'get', id:iqid})          .c('pubsub', {xmlns:Strophe.NS.PUBSUB})          .c('options', {node:node, jid:this.jid});        if (subid) iq.attrs({subid:subid});        that.addHandler(call_back, null, 'iq', null, iqid, null);        that.send(iq.tree());        return iqid;
    },

    /**
     *  Parameters:
     *    (String) node -  The name of the pubsub node.
     *    (Function) call_back - Receives subscriptions.
     *
     *  http://xmpp.org/extensions/tmp/xep-0060-1.13.html
     *  8.9 Manage Affiliations - 8.9.1.1 Request
     *
     *  Returns:
     *    Iq id
     */    getAffiliations: function(node, call_back) {        var that = this._connection;
        var iqid = that.getUniqueId("pubsubaffiliations");        if (typeof node === 'function') {            call_back = node;
            node = undefined;
        }        var attrs = {}, xmlns = {'xmlns':Strophe.NS.PUBSUB};        if (node) {            attrs.node = node;
            xmlns = {'xmlns':Strophe.NS.PUBSUB_OWNER};
        }        var iq = $iq({from:this.jid, to:this.service, type:'get', id:iqid})          .c('pubsub', xmlns).c('affiliations', attrs);        that.addHandler(call_back, null, 'iq', null, iqid, null);        that.send(iq.tree());        return iqid;
    },

    /**
     *  Parameters:
     *    (String) node -  The name of the pubsub node.
     *    (Function) call_back - Receives subscriptions.
     *
     *  http://xmpp.org/extensions/tmp/xep-0060-1.13.html
     *  8.9.2 Modify Affiliation - 8.9.2.1 Request
     *
     *  Returns:
     *    Iq id
     */    setAffiliation: function(node, jid, affiliation, call_back) {        var that = this._connection;
        var iqid = thiat.getUniqueId("pubsubaffiliations");        var iq = $iq({from:this.jid, to:this.service, type:'set', id:iqid})          .c('pubsub', {'xmlns':Strophe.NS.PUBSUB_OWNER})          .c('affiliations', {'node':node})          .c('affiliation', {'jid':jid, 'affiliation':affiliation});        that.addHandler(call_back, null, 'iq', null, iqid, null);        that.send(iq.tree());        return iqid;
    },

    /** Function: publishAtom
     */    publishAtom: function(node, atoms, call_back) {        if (!Array.isArray(atoms))            atoms = [atoms];

        var i, atom, entries = [];
        for (i = 0; i < atoms.length; i++) {            atom = atoms[i];

            atom.updated = atom.updated || (new Date()).toISOString();            if (atom.published && atom.published.toISOString)                atom.published = atom.published.toISOString();

            entries.push({                data: $build("entry", { xmlns:Strophe.NS.ATOM })                        .children(atom).tree(),                attrs:(atom.id ? { id:atom.id } : {}),
            });
        }        return this.publish(node, entries, call_back);
    },

});

效果:



标签:

上一篇: Centos 6 下安装 erlang 手记
下一篇: Qt中文乱码解决思路

相关文章暂无相关
评论列表暂无评论
发表评论
验证码

«   2024年4月   »
1234567
891011121314
15161718192021
22232425262728
2930
控制面板
您好,欢迎到访网站!
  查看权限
网站分类
搜索
最新留言
    文章归档
    网站收藏
    友情链接
    • RainbowSoft Studio Z-Blog
    • 订阅本站的 RSS 2.0 新闻聚合
    ︿
    Top