一妄壶、概述
XMPP全稱為可擴(kuò)展通訊和表示協(xié)議霞扬,是一種基于標(biāo)準(zhǔn)通用標(biāo)記語(yǔ)言的子集XML的協(xié)議,它繼承了在XML環(huán)境中靈活的發(fā)展性翼闹。 因此斑鼻,基于XMPP的應(yīng)用具有超強(qiáng)的可擴(kuò)展性。
優(yōu)勢(shì):
- 方便擴(kuò)展
- 成熟的通信協(xié)議
- 可快速部署猎荠,支持100萬(wàn)的以上在線用戶
- 成熟穩(wěn)定
- XMPP的擴(kuò)展協(xié)議Jingle使得其支持語(yǔ)音和視頻
二坚弱、基本網(wǎng)絡(luò)結(jié)構(gòu)
XMPP中定義了三個(gè)角色,客戶端关摇,服務(wù)器荒叶,網(wǎng)關(guān)。通信能夠在這三者的任意兩個(gè)之間雙向發(fā)生输虱。服務(wù)器同時(shí)承擔(dān)了客戶端信息記錄些楣,連接管理和信息的路由功能。網(wǎng)關(guān)承擔(dān)著與異構(gòu)即時(shí)通信系統(tǒng)的互聯(lián)互通悼瓮,異構(gòu)系統(tǒng)可以包括SMS(短信)戈毒,MSN,ICQ等横堡÷袷校基本的網(wǎng)絡(luò)形式是單客戶端通過(guò)TCP/IP)連接到單服務(wù)器,然后在之上傳輸XML命贴。
示例-客戶端:
<?xmlversion='1.0'?>
<stream:stream
to='example_com'
xmlns='jabber:client'
xmlns:stream='http_etherx_jabber_org/streams'
version='1.0'>
<messagefrom='juliet_example_com'
to='romeo_example_net'
xml:lang='zh-cn'>
<body>你好道宅,可以交個(gè)朋友嗎?</body>
</message>
</stream:stream>
示例-服務(wù)端:
<?xmlversion='1.0'?>
<stream:stream
from='example_com'
id='someid'
xmlns='jabber:client'
xmlns:stream='http_etherx_jabber_org/streams'
version='1.0'>
<message from='romeo_example_net'
to='juliet_example_com'
xml:lang='zh-cn'>
<body>當(dāng)然可以</body>
</message>
</stream:stream>
三胸蛛、XML使用和說(shuō)明
XMPP的協(xié)議污茵,首先以<stream></stream>標(biāo)簽作為根節(jié)點(diǎn),流的生命周期:標(biāo)簽的開(kāi)頭即是流的開(kāi)始葬项,標(biāo)簽的關(guān)閉即是流的結(jié)束泞当。
在stream標(biāo)簽中,數(shù)據(jù)通過(guò)其內(nèi)部的子標(biāo)簽(message民珍、presence襟士、iq等)當(dāng)做數(shù)據(jù)實(shí)體,通過(guò)子標(biāo)簽可以定義大量的數(shù)據(jù)嚷量。
基本節(jié)點(diǎn)
1. presence
presence節(jié)點(diǎn)本身表示通知服務(wù)器自己的狀態(tài)陋桂,用來(lái)控制和表示實(shí)體的在線狀態(tài),包含:離線(away)蝶溶、在線()嗜历、離開(kāi)、不能打擾等復(fù)雜狀態(tài),另外梨州,還能白用來(lái)創(chuàng)建和結(jié)束在線狀態(tài)的監(jiān)聽(tīng)/訂閱痕囱;
參考文檔
在節(jié)點(diǎn)中,type字段是可選暴匠,為空則表示只是通知服務(wù)器自己在線咐蝇。如果不為空, 則表示自己不在線的原因
Presence.Type.unavailable
- signals that the entity is no longer available for communication.Presence.Type.subscribe
-- the sender wishes to subscribe to the recipient's presence.Presence.Type.subscribed
-- the sender has allowed the recipient to receive their presence.Presence.Type.unsubscribe
-- the sender is unsubscribing from another entity's presence.Presence.Type.unsubcribed
-- the subscription request has been denied or a previously-granted subscription has been cancelled.Presence.Type.probe
-- a request for an entity's current presence; SHOULD be generated only by a server on behalf of a user.Presence.Type.error
-- an error has occurred regarding processing or delivery of a previously-sent presence stanza.
1.1.子屬性
Show
用于指定實(shí)體或特定資源的特定可用性狀態(tài)巷查,在presence中只能存在一個(gè)。數(shù)據(jù)值只能是以下之一:
- away 表示用戶暫時(shí)離開(kāi)
- chat 表示用戶正在聊天
- dnd 表示用戶處于請(qǐng)忽打擾=Do Not Disturb
- xa 表示用戶處于長(zhǎng)時(shí)間離線=eXtended Away抹腿,不在線
<presence>
<show>away</show> <!--離線-->
</presence>
1.2. Status
針對(duì)可用性的描述岛请,通常和Show元素一起使用(例如:在會(huì)議中)。
<presence>
<show>away</show> <!--離線-->
<status>atthe ball</status> <!--標(biāo)簽用于顯示額外信息-->
</presence>
1.3. Priority
用于指定資源的優(yōu)先級(jí)警绩。值在-128到127之間崇败。
2. message
講限定消息推送到聊天對(duì)話或多用戶聊天室的消息實(shí)體。
2.1 主要屬性
屬性 | 說(shuō)明 |
---|---|
from | 發(fā)送信息自身的Full ID肩祥。 |
to | 設(shè)置信息接收方的Bare JID后室,通常在第一次發(fā)送方無(wú)法得知接收方的Full JID,通過(guò)服務(wù)器中轉(zhuǎn)路由根據(jù)Base JID映射接收方的Full JID混狠,盡量在to屬性中包含對(duì)方的完成Full JID岸霹,減少服務(wù)器的接入定位。 |
id | 該屬性僅用于接收實(shí)體發(fā)送給初始化實(shí)體 XML流的頭将饺。這個(gè)屬性是一個(gè)由接收實(shí)體創(chuàng)建的具有唯一性的ID,一個(gè)初始實(shí)體和接收實(shí)體之間的會(huì)話ID贡避,并且它在接收方的應(yīng)用程序中必須是唯一的。注意:這個(gè)流 ID 必須是足夠安全的予弧,所以它必須是不可預(yù)知的和不可重復(fù)的刮吧。它不應(yīng)該在有 'id'屬性出現(xiàn)在初始實(shí)體發(fā)送給接收實(shí)體的 XML流的頭中;無(wú)論如何掖蛤,如果'id'屬性出現(xiàn)在初始化流中杀捻,接收實(shí)體應(yīng)該忽略它。 |
xml:lang | 用于實(shí)現(xiàn)國(guó)際化的屬性蚓庭,表示發(fā)送的XML字符所使用的語(yǔ)言致讥。 |
version | 代表當(dāng)前XML實(shí)體的版本 |
type | 聊天類型 |
JID的結(jié)構(gòu):在XMPP網(wǎng)絡(luò)上,每一個(gè)實(shí)體都有一個(gè)JID標(biāo)識(shí)彪置,JID是一組排列好的元素拄踪,包括域名(domain identifier),節(jié)點(diǎn)名(node identifier)拳魁,和資源名(resource identifier)惶桐。如:jid = [ node "@" ] domain [ "/" resource ]
- node:是對(duì)用戶的抽象,既可以代表一個(gè)真實(shí)的用戶,也能表示一個(gè)虛擬用戶如一個(gè)聊天室等姚糊。
- domain:表達(dá)了客戶所連接的服務(wù)器贿衍,在實(shí)踐中通常表示一個(gè)特定的集群,由同一domain來(lái)表示救恨。
- resource:它通常表示一個(gè)特定的會(huì)話贸辈,連接。對(duì)于服務(wù)器和和其他客戶端來(lái)說(shuō)肠槽,資源名是不透明的擎淤。
2.2 type
聊天類型:
- chat -- 在一對(duì)一聊天對(duì)話的上下文中發(fā)送聊天信息,存在歷史記錄秸仙。
- error -- 表示通知發(fā)送者錯(cuò)誤的原因嘴拢,一般由服務(wù)端發(fā)送給客戶端。
- groupchat -- 用于多人聊天室的消息發(fā)送寂纪,存在歷史記錄席吴。
- headline -- 提供發(fā)送指定客戶端或廣播內(nèi)容(系統(tǒng)通知、警告捞蛋、實(shí)時(shí)數(shù)據(jù)更新)所采用的類型孝冒。具有很高的實(shí)時(shí)性,預(yù)期是不需要收到回復(fù)的信息拟杉。
- normal -- 是獨(dú)立于一對(duì)一消息和多人聊天之外的消息庄涡,預(yù)期是有信息回復(fù)的,但是沒(méi)有歷史記錄的產(chǎn)生搬设,一般用于系統(tǒng)強(qiáng)制用戶確認(rèn)或取消等啼染。
2.3 子標(biāo)簽
- <subject/>
- <body/>
- <thread/>
- <delay/>
2.3.1 subject
表示一個(gè)消息體的主題內(nèi)容,通常在聊天窗口標(biāo)題處焕梅。除了xml:lang之外迹鹅,不會(huì)有其他屬性。該元素可以有多個(gè)(每個(gè)lang不能重復(fù)贞言,只能存在一個(gè))斜棚,方便不同語(yǔ)言不通的主題 。
<message
to='romeo@example.net'
from='juliet@example.com/balcony'
type='chat'
xml:lang='en'>
<subject>I implore you!</subject>
<subject
xml:lang='cz'>Úpěnlivě prosim!</subject>
<body>Wherefore art thou, Romeo?</body>
<body xml:lang='cz'>PročeŽ jsi ty, Romeo?</body>
</message>
2.3.2 body
指定消息的文本內(nèi)容该窗。除了xml:lang之外弟蚀,不會(huì)有其他屬性。該元素可以有多個(gè)(每個(gè)lang不能重復(fù)酗失,只能存在一個(gè))义钉,方便不同語(yǔ)言不通的顯示 。
<message
to='romeo@example.net'
from='juliet@example.com/balcony'
type='chat'
xml:lang='en'>
<body>Wherefore art thou, Romeo?</body>
<body xml:lang='cz'>PročeŽ jsi ty, Romeo?</body>
</message>
2.3.3 thread
用于跟蹤一個(gè)會(huì)話规肴,該元素主要用于客戶端實(shí)現(xiàn)消息展示(例如:消息歷史查詢時(shí)捶闸,每次會(huì)話折疊顯示消息)夜畴,每次會(huì)話會(huì)產(chǎn)生一個(gè)唯一的thread.id,xmpp推薦采用uuid算法删壮。不同時(shí)間段的聊天內(nèi)容贪绘,可能是基于多個(gè)會(huì)話,在查詢聊天記錄的時(shí)候央碟,可以根據(jù)會(huì)話ID進(jìn)行折疊顯示税灌。
<message
to='juliet@example.com/balcony'
from='romeo@example.net/orchard'
type='chat'
xml:lang='en'>
<body>Neither, fair saint, if either thee dislike.</body>
<thread>e0ffe42b28561960c6b12b944a092794b9683a38</thread>
</message>
2.3.4 delay
表示該消失是一個(gè)離線消息。 <delay>子元素的from記錄了延遲消息的最后來(lái)源方亿虽,如上例中from為capulet.com指接收離線消息人連接的服務(wù)器菱涤,離線消息最終由該服務(wù)器發(fā)出
stamp屬性記錄了離線消息的存儲(chǔ)時(shí)間,客戶端實(shí)現(xiàn)應(yīng)顯示該時(shí)間而非接收到的時(shí)間洛勉。
<message from='romeo@montague.net/orchard' to='juliet@capulet.com'>
<body>
收到狸窘,請(qǐng)回復(fù)信息。
</body>
<delay xmlns='urn:xmpp:delay'
from='capulet.com'
stamp='2002-09-10T23:08:25Z'>Offline Storage</delay>
</message>
3. IQ
iq節(jié)點(diǎn)主要是用于Info/Query模式的消息請(qǐng)求坯认,類似于HTTP的get/post請(qǐng)求,可以發(fā)出get以及set請(qǐng)求氓涣,期望有返回值(有result,error兩種回應(yīng))牛哺。
type屬性值:
- Get: 獲取當(dāng)前域值
- Set: 設(shè)置替換get查詢的值
- Result: 說(shuō)明成功相應(yīng)了先前的查詢
- Error: 查詢或相應(yīng)時(shí)候出現(xiàn)了錯(cuò)誤
<iq to='example.com'
type='set'
id='sess_1'>
<session xmlns='urn:ietf:params:xml:ns:xmpp-session'/>
</iq>
4. 通信流程
使用Stream元素,用來(lái)表示客戶端-服務(wù)器-客戶端的通信建立劳吠,在建立通信時(shí)引润,需要保證以下兩點(diǎn):
在創(chuàng)建會(huì)話或者發(fā)送信息之前,必須完成流的認(rèn)證痒玩。
在完成流的認(rèn)證之后淳附,客戶端必須將資源綁定到流,如客戶端地址<user@domain/resource>
服務(wù)端向客戶端通知會(huì)話建立功能蠢古;
<stream:stream
xmlns='jabber:client'
xmlns:stream='http://etherx.jabber.org/streams'
id='c2s_345'
from='example.com'
version='1.0'>
<stream:features>
<bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'/>
<session xmlns='urn:ietf:params:xml:ns:xmpp-session'/>
</stream:features>
- 1奴曙、客戶端向服務(wù)端發(fā)起會(huì)話請(qǐng)求,并指定一個(gè)綁定的資源名:
<iq to='example.com'
type='set'
id='sess_1'>
<session xmlns='urn:ietf:params:xml:ns:xmpp-session'/>
<bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'>
<resource>pc-win-someone</resource>
</bind>
</iq>
- 2草讶、服務(wù)端響應(yīng)資源綁定請(qǐng)求洽糟,并返回綁定后的Full JID名:
<iq from='example.com'
type='result'
id='sess_1'>
<bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'>
<jid>somenode@example.com/pc-win-someone-server-gen-random-string</jid>
</bind>
</iq>
在會(huì)話創(chuàng)建有幾種錯(cuò)誤可能:
- 2.1、服務(wù)內(nèi)部錯(cuò)誤 error:type="wait"
- 2.2堕战、賬戶錯(cuò)誤或者賬戶不允許會(huì)話創(chuàng)建 error:type="auth"
- 2.3坤溃、會(huì)話沖突 error:type="cancel"
資源綁定:
- 標(biāo)識(shí)客戶端的平臺(tái)
- 服務(wù)器為每個(gè)客戶端生成隨機(jī)值,生成唯一后綴嘱丢,用于區(qū)分不同的客戶端連接
- 相同賬戶的多點(diǎn)登錄(多個(gè)終端登錄)薪介,通過(guò)resource區(qū)分同一用戶的不同接入點(diǎn),方便策略的執(zhí)行
完整數(shù)據(jù)流
1: 客戶端初始化流給服務(wù)器:
<stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' to='example.com' version='1.0'>
2: 服務(wù)器發(fā)送一個(gè)流標(biāo)簽給客戶端作為應(yīng)答:
<stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' id='c2s_123' from='example.com' version='1.0'>
3: 服務(wù)端發(fā)送TLS流特征說(shuō)明(包括驗(yàn)證機(jī)制和任何其他流特性):
<stream:features>
<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'>
<required/>
</starttls>
<mechanisms xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>
<mechanism>DIGEST-MD5</mechanism>
<mechanism>PLAIN</mechanism>
</mechanisms>
</stream:features>
4: 客戶端發(fā)送 TLS握手給服務(wù)器:
<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>
5: 服務(wù)器通知客戶端可以繼續(xù)進(jìn)行:
<proceed xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>
(或者): 服務(wù)器通知客戶端 TLS 握手失敗并關(guān)閉流和TCP連接:
<failure xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>
</stream:stream>
6: 客戶端和服務(wù)器嘗試通過(guò)已有的TCP連接完成 TLS 握手. resume是否允許恢復(fù)會(huì)話
<enabled xmlns='urn:xmpp:sm:3' id='some-long-sm-id' resume='true'/>
7: 如果 TLS 握手成功, 客戶端初始化一個(gè)新的流給服務(wù)器:
<stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' to='example.com' version='1.0'>
(或者): 如果 TLS 握手不成功, 服務(wù)器關(guān)閉 TCP 連接.
8: 服務(wù)器發(fā)送一個(gè)加密流初始化給客戶端越驻,其中包括任何可用的流特性:
<stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' from='example.com' id='c2s_234' version='1.0'>
8.1:服務(wù)端發(fā)送SASL特征說(shuō)明汁政,mechanism支出了服務(wù)支持的認(rèn)證機(jī)制道偷,有關(guān)SASL認(rèn)證機(jī)制[RFC4422]
<stream:features>
<mechanisms xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>
<mechanism>DIGEST-MD5</mechanism>
<mechanism>PLAIN</mechanism>
<mechanism>EXTERNAL</mechanism>
</mechanisms>
</stream:features>
9: 客戶端繼續(xù) SASL 握手
<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='PLAIN'>AGp1bGlldAByMG0zMG15cjBtMzA=</auth>
5. 多用戶文本聊天協(xié)議(Multi User Chat)
XMPP在其XEP-0045擴(kuò)展中定義了一個(gè)用于多用戶文本會(huì)議(群聊)的協(xié)議,類似于聊天室烂完、QQ群等试疙。由于它作為一個(gè)標(biāo)準(zhǔn)協(xié)議在定義模型上力求完備,涵蓋了現(xiàn)實(shí)中的絕大部分IM產(chǎn)品模型抠蚣,而現(xiàn)實(shí)中的IM產(chǎn)品基本都只實(shí)現(xiàn)了XMPP定義的模型中的一個(gè)子集祝旷。
XMPP定義的一些基本概念:
- 房間:房間的JID標(biāo)識(shí)room@service,room標(biāo)識(shí)房間的名稱或者ID嘶窄,service是服務(wù)器地址
- 訪客: 訪客JID<room@service/nick>怀跛,nick是訪客在房間的昵稱
- 崗位:表達(dá)了用戶和房間的長(zhǎng)期關(guān)系。XMPP定義的崗位有:所有者(owner)柄冲、管理者(admin)吻谋、成員(member)、排斥者(outcast)
- 角色:表達(dá)了用戶和房間的臨時(shí)聯(lián)系现横,它只存在與一次訪問(wèn)期間漓拾。角色:主持人(moderator)、與會(huì)者(paticipant)戒祠、游客(visitor)
XMPP MUC協(xié)議擴(kuò)展定義了一個(gè)廣泛的用例集合骇两,下面提取一些典型的核心場(chǎng)景來(lái)簡(jiǎn)要分析說(shuō)明并輔助實(shí)現(xiàn)。
- MUC服務(wù)發(fā)現(xiàn)
主要用于客戶端向服務(wù)器咨詢是否支持MUC,協(xié)議交互細(xì)節(jié)詳見(jiàn):MUC Discovering - 新建房間
從房間創(chuàng)建的視角來(lái)看,本質(zhì)上有2種類型的房間:
instant room 臨時(shí)房間(類似于臨時(shí)會(huì)話)想罕,適用于那些臨時(shí)選取多個(gè)用戶進(jìn)行會(huì)話的場(chǎng)景
reserverd room 永久房間(類似于固定群) - 銷毀房間
銷毀房間通常僅限于房間的所有者疚漆,臨時(shí)房間通常是在房間所有用戶都離開(kāi)后自動(dòng)銷毀 - 加入房間
加入房間可以有2種方式,申請(qǐng)和邀請(qǐng) - 發(fā)言
在房間內(nèi)發(fā)言方式從使用場(chǎng)景的角度看通常有3種:
- 向房間內(nèi)所有人發(fā)言,發(fā)言者發(fā)送一個(gè)消息類型為groupchat的消息,由房間服務(wù)轉(zhuǎn)發(fā)給所有與會(huì)者。
- 向部分人發(fā)言难审,這個(gè)場(chǎng)景發(fā)言者實(shí)際創(chuàng)建了一個(gè)臨時(shí)房間,在該臨時(shí)房間內(nèi)進(jìn)行群發(fā)亿絮。
- 向某一個(gè)人發(fā)送似有消息剔宪,這個(gè)場(chǎng)景退化為了一對(duì)一的單獨(dú)聊天。
- 退出房間
主動(dòng)退出壹无、管理員(主持人)踢出房間