-
- Strophe對像中
- 命名空間
- 連接狀態
- 日誌
- addConnectionPlugin 添加插件
- forEachChild
- 示例,解析Vcard節
- isTagEqual
- xmlescape,xmlunescape,serialize
- escapeNode,unescapeNode
- 與JID相關的幾個方法getNodeFromJid,getDomainFromJid,getResourceFromJid,getBareJidFromJid
- Strophe.Builder
- 示例
- Strophe.Handler和trophe.TimeHandler
- SASL
- Strophe.SASLMechanism 驗證機制
- Strophe.Connection
- connect, disconnect斷開連接。
- send(elem) 發送節, sendIQ(elem, callback, errback, timeout) 發送IQ節
- addHandler,deleteHandler,addTimedHandler,deleteTimedHandler
- xmlInput,xmlOutput,rawInput,rawOutput
- Strohpe.Websocket,Strophe.Bosh 和Strophe.Request
- Strophe對像中
Strohpe1.2.7.API的介紹。Strophe是使用JS實現的與XMPP服務器進行連接、發送、接收消息的WEB端底層插件。
- Builder
- Handler
- TimeHandler
- Connection
- SASLMechanism
- SASLPlain
- SASLSHA1
- SASLMD5
- SASLOAuthBearer
- Request
- Bosh
- WebSocket
或者可以通過代碼來查看,最後暴露了些什麼到全局中。
var o = factory(root.SHA1, root.Base64, root.MD5, root.stropheUtils); window.Strophe = o.Strophe; window.$build = o.$build; window.$iq = o.$iq; window.$msg = o.$msg; window.$pres = o.$pres; window.SHA1 = o.SHA1; window.Base64 = o.Base64; window.MD5 = o.MD5; window.b64_hmac_sha1 = o.SHA1.b64_hmac_sha1; window.b64_sha1 = o.SHA1.b64_sha1; window.str_hmac_sha1 = o.SHA1.str_hmac_sha1; window.str_sha1 = o.SHA1.str_sha1;
分類一下:
- Strophe
- Builder 用來構建節,已經提供了三種基本的構建方式
- iq 請求響應節
- msg 消息節
- pre 出席節
- Handler和TimeHandler
- Connection
- 通訊通道一:Bosh
- Request Bosh通道不是真正意義上的長連接,通過不斷發起請求來模擬長連接
- 通訊通道二:WebSocket,真正意義上的長連接。
- 登錄加密:作為Connection登錄時加密所用。
- SASLMechanism
- SASLPlain
- SASLSHA1
- SASLMD5
- SASLOAuthBearer
- 通訊通道一:Bosh
Strophe對像中
命名空間
對應協議中定義的命名空間。NS對象指定了一些Strophe中使用到的命名空間
NS: {
HTTPBIND: "http://jabber.org/protocol/httpbind",
BOSH: "urn:xmpp:xbosh",
CLIENT: "jabber:client",
AUTH: "jabber:iq:auth",
ROSTER: "jabber:iq:roster",
PROFILE: "jabber:iq:profile",
DISCO_INFO: "http://jabber.org/protocol/disco#info",
DISCO_ITEMS: "http://jabber.org/protocol/disco#items",
MUC: "http://jabber.org/protocol/muc",
SASL: "urn:ietf:params:xml:ns:xmpp-sasl",
STREAM: "http://etherx.jabber.org/streams",
FRAMING: "urn:ietf:params:xml:ns:xmpp-framing",
BIND: "urn:ietf:params:xml:ns:xmpp-bind",
SESSION: "urn:ietf:params:xml:ns:xmpp-session",
VERSION: "jabber:iq:version",
STANZAS: "urn:ietf:params:xml:ns:xmpp-stanzas",
XHTML_IM: "http://jabber.org/protocol/xhtml-im",
XHTML: "http://www.w3.org/1999/xhtml"
},
同時,如果我們需要擴展命名空間,可以使用addNamespace方法。
addNamespace: function (name, value) {
Strophe.NS[name] = value;
},
連接狀態
登錄結果各種狀態。
Status: {
ERROR: 0, // 错误
CONNECTING: 1, // 连接中
CONNFAIL: 2, // 连接失败
AUTHENTICATING: 3, // 认证中
AUTHFAIL: 4, // 认证失败
CONNECTED: 5, // 已连接
DISCONNECTED: 6, // 已断开连接
DISCONNECTING: 7, // 断开连接中
ATTACHED: 8, // 附加
REDIRECT: 9, // 重定向
CONNTIMEOUT: 10 // 连接超时
},
日誌
Strophe還提供了一個Log實現,當然,只是一個小實現。
我們要通過重寫Strophe.log方法來
LogLevel: {
DEBUG: 0,
INFO: 1,
WARN: 2,
ERROR: 3,
FATAL: 4
},
/**
*
* 函数:log
用户重写打日志函数
默认的实现没有做任何事情
如果客户端代码想要去处理日志消息,应该重写这个函数:
Strophe.log = function(level, msg) {
// user code here
};
以下是不同的等级和它们所代表的意义
参数:
数值型 level - 日志等级,
字符串 msg - 日志内容
*/
log: function (level, msg) {
return;
},
debug: function(msg){
this.log(this.LogLevel.DEBUG, msg);
},
info: function (msg) {
this.log(this.LogLevel.INFO, msg);
},
warn: function (msg) {
this.log(this.LogLevel.WARN, msg);
},
error: function (msg) {
this.log(this.LogLevel.ERROR, msg);
},
fatal: function (msg) {
this.log(this.LogLevel.FATAL, msg);
},
addConnectionPlugin 添加插件
Strophe的一些其它插件通過該方法加入Strophe中。
/**
* 扩展Strophe.Connection使之能够接受给定的插件
* 参数
* name - 插件的名称
* ptype - 插件的标准|原型
*/
addConnectionPlugin: function (name, ptype) {
Strophe._connectionPlugins[name] = ptype;
}
forEachChild
/**
*
* elem 传入的元素
* eleName 子元素标签名过滤器,注意这个是区分大小写的。如果elemName是空的,所有子元素都会被通过这个函数的执
* 否则只有那些标签名称能够匹配eleName的才会被执行
* func 找到符合的每个子元素都会调用一次该函数,该函数参数为一个符合要求的DOM类型的元素
*/
forEachChild: function (elem, elemName, func) {
var i, childNode;
for (i = 0; i < elem.childNodes.length; i++) {
childNode = elem.childNodes[i];
if (childNode.nodeType == Strophe.ElementType.NORMAL &&
(!elemName || this.isTagEqual(childNode, elemName))) {
func(childNode);
}
}
},
示例,解析Vcard節
- 可以這麼做,但是如果用JQuery來做更方便。
/**
* 将Vcard节转为Vcard对象。
*/
parseVcardStanzaToVcard : function(stanza) {
// 前面这几行可以忽略,主要是展示后面使用forEachChild方法。
var $stanza = $(stanza);
var vcardTemp = new XoW.Vcard();
var jid = $stanza.attr('from');
vcardTemp.jid = jid;
var $vcard = $stanza.find('vCard');
// stanza就是服务器返回的未经处理的Vcard节,
Strophe.forEachChild(stanza, "vCard", function(vcard) {
// 解析出该DOM元素中标签为vCard的DOM元素,即<vCard>...</vCard>
Strophe.forEachChild(vcard, "N", function(N) {
// 从Vcard元素中标签为N的元素,即<N>...</N>
Strophe.forEachChild(N, "FAMILY", function(FAMILY) {
// 解析出N标签中的<FAMILY></FAMILY>元素。
vcardTemp.N.FAMILY = FAMILY.textContent;
});
Strophe.forEachChild(N, "GIVEN", function(GIVEN) {
// 解析出N标签中的<GIVEN></GIVEN>元素。
vcardTemp.N.GIVEN = GIVEN.textContent;
});
Strophe.forEachChild(N, "MIDDLE", function(MIDDLE) {
// 解析出N标签中的<MIDDLE></MIDDLE>元素。
vcardTemp.N.MIDDLE = MIDDLE.textContent;
});
});
Strophe.forEachChild(vcard, "ORG", function(ORG) {
Strophe.forEachChild(ORG, "ORGNAME", function(ORGNAME) {
vcardTemp.ORG.ORGNAME = ORGNAME.textContent;
});
Strophe.forEachChild(ORG, "ORGUNIT", function(ORGUNIT) {
vcardTemp.ORG.ORGUNIT = ORGUNIT.textContent;
});
});
// ... 省略其他代码
});
return vcardTemp;
},
isTagEqual
/**
* 判断某个元素的标签名
* e1 一个dom元素
* name 元素的名字
*/
isTagEqual: function (el, name) {
return el.tagName == name;
},
xmlescape,xmlunescape,serialize
- xmlescape,xmlunescape 用於轉義標籤
- serialize用於轉義整個DOM,使之可以打印在HTML中,一般我們要打印DOM的時候可以用這個。
xmlescape: function(text)
{
text = text.replace(/\&/g, "&");
text = text.replace(/</g, "<");
text = text.replace(/>/g, ">");
text = text.replace(/'/g, "'");
text = text.replace(/"/g, """);
return text;
},
xmlunescape: function(text)
{
text = text.replace(/\&/g, "&");
text = text.replace(/</g, "<");
text = text.replace(/>/g, ">");
text = text.replace(/'/g, "'");
text = text.replace(/"/g, "\"");
return text;
},
serialize: function (elem) {
var result;
// 省略...
return result;
},
escapeNode,unescapeNode
JID因為它的格式,其中包含/和@符號,在有些地方可能不能直接使用。使用這個方法可以轉義它。
/**
* 对jid的node部分进行换码,node@domain/resource
*
* 参数
* node - 一个node
* 返回值
* 换码后的node
*/
escapeNode: function (node) {
if (typeof node !== "string") { return node; }
return node.replace(/^\s+|\s+$/g, '')
.replace(/\\/g, "\\5c")
.replace(/ /g, "\\20")
.replace(/\"/g, "\\22")
.replace(/\&/g, "\\26")
.replace(/\'/g, "\\27")
.replace(/\//g, "\\2f")
.replace(/:/g, "\\3a")
.replace(/</g, "\\3c")
.replace(/>/g, "\\3e")
.replace(/@/g, "\\40");
},
unescapeNode: function (node) {
if (typeof node !== "string") { return node; }
return node.replace(/\\20/g, " ")
.replace(/\\22/g, '"')
.replace(/\\26/g, "&")
.replace(/\\27/g, "'")
.replace(/\\2f/g, "/")
.replace(/\\3a/g, ":")
.replace(/\\3c/g, "<")
.replace(/\\3e/g, ">")
.replace(/\\40/g, "@")
.replace(/\\5c/g, "\\");
},
與JID相關的幾個方法getNodeFromJid,getDomainFromJid,getResourceFromJid,getBareJidFromJid
/**
* 从JID中获得node部分。node@domain/resource
* 参数
* jid
* 返回值
* node
*/
getNodeFromJid: function (jid) {
if (jid.indexOf("@") < 0) { return null; }
return jid.split("@")[0];
},
/**
* 从JID中得到domain
* 参数
* jid
* 返回值
* node
*/
getDomainFromJid: function (jid) {
var bare = Strophe.getBareJidFromJid(jid);
if (bare.indexOf("@") < 0) {
return bare;
} else {
var parts = bare.split("@");
parts.splice(0, 1);
return parts.join('@');
}
},
/**
* 从JID中得到resource部分
*
*/
getResourceFromJid: function (jid) {
var s = jid.split("/");
if (s.length < 2) { return null; }
s.splice(0, 1);
return s.join('/');
},
/**
* 得到纯JID
*/
getBareJidFromJid: function (jid) {
return jid ? jid.split("/")[0] : null;
},
Strophe.Builder
這個對象提供了一個和JQuery很像的接口,但是構建DOM元素更容易和快速,
所有的函數返回值都是對像類型的,除了toString()和tree(),
所以可以鍊式調用,這裡是一個使用$iq()構建者的例子
方法
- Builder(name, attrs) 構造函數,兩個參數:該節名稱,節的屬性
- tree() 返回當前節的DOM對象。返回的對象可以使用Strophe.Connection.send()方法直接發送給服務端
- up()返回上一級
- toString() 返回當前節的字符串形式
- attrs() 添加或者修改當前元素的屬性
- c(name, attrs, text) 添加節點。參數分別是:節點名稱,節點屬性,節點文本內容。
- cnode(elem) 這個函數和c()函數以一樣,除了它不是使用name 和attrs 去創建
- t(text) 添加一個文本子元素
- h(html) 用HTML替換當前的元素內容
已經默認提供了三種基本的節:
- 消息節function $msg(attrs) { return new Strophe.Builder(“message”, attrs); }
- iq節function $iq(attrs) { return new Strophe.Builder(“iq”, attrs); }
- 出席節function $pres(attrs) { return new Strophe.Builder(“presence”, attrs); }
示例
$iq({to: 'you', from: 'me', type: 'get', id: '1'})
.c('query', {xmlns: 'strophe:example'})
.c('example')
.toString();
以上的代碼會生成如下的XML片段
<iq to='you' from='me' type='get' id='1'>
<query xmlns='strophe:example'>
<example/>
</query>
</iq>
Strophe.Handler和trophe.TimeHandler
這兩個是Strophe內部使用的。
如下是Handler構造函數,我們不直接使用這兩個對象。
参数:
函数 handler - 触发式执行的函数
字符串 ns - 需要匹配的命名空间
字符串 name - 需要匹配的name
字符串 type - 同上
字符串 id - 同上
字符串 from - 同上
字符串 options - 处理器选项
Handler (handler, ns, name, type, id, from, options)
但是當我們監聽Strophe中是否接收到節的時候,使用Strophe.Connection.addHandler()和Strophe.Connection.deleteHandler()間接使用。TimeHandler也是一樣的。
addHandler: function (handler, ns, name, type, id, from, options) {
var hand = new Strophe.Handler(handler, ns, name, type, id, from, options);
this.addHandlers.push(hand);
return hand;
},
SASL
Strophe.SASLMechanism 驗證機制
優先級,所以如果沒有設置,默認驗證就是用SCRAM-SHA1。
* SCRAM-SHA1 - 40 * DIGEST-MD5 - 30 * Plain - 20
Strophe.Connection
我們的程序主要使用Connection類,用它來建立連接,發送節等操作。
通道有兩種,WebSocket和Bosh。他們對Connection提供了相同的接口。所以在使用Connection時,它會根據你在創建Connection對象時傳進來的參數,判斷需要使用的通道類型。
構造方法中,有這麼一段。其中service就是服務器的地址,如果傳進來的服務器地址以ws:或者wss:開頭,那麼就會使用Websocket作為通道,否則使用Bosh。
Connection (service, options) {
// ....省略其他代码
if (service.indexOf("ws:") === 0 || service.indexOf("wss:") === 0 ||
proto.indexOf("ws") === 0) {
this._proto = new Strophe.Websocket(this); // proto - WS对象
} else {
this._proto = new Strophe.Bosh(this);
}
// ....省略其他代码
}
connect, disconnect斷開連接。
// jid,密码,登录结果回调。
connect(jid, pass, callback, wait, hold, route, authcid) {
下面我寫的一段代碼,我的connect方法中,在新建了new Strophe.Connection(serviceURL);後,使用connect與服務器建立連接
connect : function(serviceURL, jid, pass) {
XoW.logger.ms(this.classInfo + "connect()");
// 新建一个Strophe.Connection对象,此时并未开始连接服务器
this._stropheConn = new Strophe.Connection(serviceURL);
// 重写Stroope的rowInput/Ouput方法用于打印报文。
this._rawInputOutput();
// 连接服务器
this._stropheConn.connect(jid, pass, this._connectCb.bind(this));
XoW.logger.me(this.classInfo + "connect()");
},
send(elem) 發送節, sendIQ(elem, callback, errback, timeout) 發送IQ節
- send,發送消息節和出席節的時候使用這個。
- sendIQ,建議在發送IQ節的時候使用這個。因為iq節一定有一個返回的節。用一個回調去接受響應的節。
示例:發送在線出席節
var p1 = $pres({
id : XoW.utils.getUniqueId("presOnline")// 获得唯一的id
}).c("status")
.t("在线")
.up()
.c("priority")
.t('1');
this._gblMgr.getConnMgr().send(p1);
1
示例:發送請求vcard節。
getVcard : function(jid, successCb, errorCb, timeout) {
if(!jid) {
jid = null;
}
// 没有带from属性,服务器也能知道是“我”发送的,服务器中有做处理。
vcard = $iq({
id : XoW.utils.getUniqueId("getVcard"),
type : "get",
to : jid
}).c("vCard", {
xmlns : XoW.NS.VCARD
});
this._gblMgr.getConnMgr().sendIQ(vcard, function(stanza) {
// 成功获取vcard处理
}.bind(this), function(errorStanza) {
// 错误处理
}, timeout);
},
addHandler,deleteHandler,addTimedHandler,deleteTimedHandler
addHandler監聽節。我們通過這個方法,監聽從服務器發送來的節。
/**
* 添加监听器
* ns - 要匹配的命名空间
* name - 同上
* type - 匹配节的类型
* id - 匹配节的id
* from - 匹配节的from
* options 处理器的可选项
*/
addHandler: function (handler, ns, name, type, id, from, options) {
var hand = new Strophe.Handler(handler, ns, name, type, id, from, options);
this.addHandlers.push(hand);
return hand;
},
deleteHandler: function (handRef) {
// this must be done in the Idle loop so that we don't change
// the handlers during iteration
this.removeHandlers.push(handRef);
// If a handler is being deleted while it is being added,
// prevent it from getting added
var i = this.addHandlers.indexOf(handRef);
if (i >= 0) {
this.addHandlers.splice(i, 1);
}
},
addTimedHandler: function (period, handler) {
var thand = new Strophe.TimedHandler(period, handler);
this.addTimeds.push(thand);
return thand;
},
deleteTimedHandler: function (handRef) {
// this must be done in the Idle loop so that we don't change
// the handlers during iteration
this.removeTimeds.push(handRef);
},
比如
// 若不指定具体参数,只给定回调函数,那么监听所有从服务器发送来的节 this._gblMgr.getConnMgr().addHandler(this._needDealCb.bind(this)); // 监听presence节,第一个参数是我的回调函数。 this._gblMgr.getConnMgr().addHandler(this._presenceCb.bind(this), null, "presence");
xmlInput,xmlOutput,rawInput,rawOutput
這四個方法,是發送接收節過程中必定會調用到了,它們都是空實現,這個的作用是讓我們實現他們,來做一些自己想做的事,比如打印每次發送接收的節到控制台,方便調試。
這下面這段是我自定義的一個方法,裡面重寫了rawInput和rawOutput這兩個方法,然後調用自己的日誌類,打印他們。
_rawInputOutput : function() {
XoW.logger.ms(this.classInfo + "_rawInputOutput()");
this._stropheConn.rawInput = function(data) {
XoW.logger.receivePackage(data);
}.bind(this);
this._stropheConn.rawOutput = function(data) {
XoW.logger.sendPackage(data);
}.bind(this);
XoW.logger.me(this.classInfo + "_rawInputOutput()");
},
Strohpe.Websocket,Strophe.Bosh 和Strophe.Request
Bosh和Websocket類作為底層連接類,提供一些連接方法的處理。我們的關注點主要是在Connection類。
使用插件時並不會調用到這邊的方法。