Presence
在XMPP協定中,我們使用presence來擷取使用者是否已經上線以及是否可以通信的狀态。
為了能夠知道自己聯系人的狀态以及讓聯系人知道自己的狀态,使用者上線後需要訂閱聯系人的狀态,聯系人也同樣需要訂閱使用者的狀态。
通過下面的消息訂閱聯系人的狀态:
<presence from="[email protected]" to="[email protected]" type="subscribe"/>
當聯系人接收/拒絕訂閱時,會發送消息的消息體(sucribed/unsubscribe)回應。
通常用戶端是自動回應這些消息的,當我們訂閱了聯系人的狀态之後,也會受到聯系人的狀态變更資訊。
還可以通過嵌入<show/>(chat/away/xa/dnd)和<status/>元素表示更加豐富的資訊。
<presence>
<show>away</show>
<status>Having a spot of tea</status>
</presence>
需要注意的是presence節是占用帶寬最多的節,任何對該節内容的擴充都需要慎重。
另外,presence還提供priority屬性用來辨別資源的優先級(-127-128),負數優先級的資源将無法接收消息,除非顯式指定,這個特性通常是由伺服器實作的。
另外,我們可以通過發送directed presence到其他使用者,來避免訂閱對方資訊,非常适合應用于網絡而上的簡短交流。
可以通過發送<presence type=”unavailable” />發送下線通知,伺服器會完成消息儲存,聯系人通知等一系列操作。
presence也有其富文本形式,可以包含更多資訊,但不建議在presence中使用(資源和帶寬)。Publish-Subscribe[XEP-0060]和Personal Eventing Protocal[XEP-0163]提供了類似功能,但presence是針對整個花名冊廣播的。
伺服器傳回的花名冊中還可以包含更加豐富的資訊,包括使用者組以及訂閱情況。
使用者對花名冊的修改也可以通過發送IQ-set(rost-push)同步到伺服器及其他用戶端上(使用者可能有手機/pad等)。這種隻推送變更的機制可以簡化用戶端程式設計并節省流量。
Instant Messaging
消息發送流程
- user1向user2發送消息,user1位于domain1,user2位于domain2
- user1的client1向server1發送消息節,server1設定from屬性
- server1投遞消息給server2(直接通信)
- server2收到消息并檢查user2是否線上并投遞
- normal
message消息類型
獨立消息,将會馬上投遞或者緩存,是預設消息類型
- chat
使用者聊天,通過session建立,通常處理一系列消息
- groupchat
多人聊天,通常此類型會指定一個元件或者子產品處理多人聊天,該子產品會為每個參與者發送消息
- headline
全體通知,不會緩存,立即投遞
- error
錯誤資訊節,回報錯誤資訊
延時投遞
如果使用者不線上,則消息被緩存,使用者登入後,消息推送給使用者,并攜帶消息原始産生時間,用于用戶端對消息進行排序。
可以參考Delayed Delivery[XEP-0203]
Chat session
chat session用于使用者頻繁交流的情況,這類似于現實情況中的聊天,其建立過程為:雙方使用者在消息互動中知道了對方的Full JID,是以可以直接通信,響應的機制稱為chat session。
狀态通知
類似于QQ的“正在輸入”功能,讓互動雙方了解即時狀态。
該功能擴充由XEP-0085定義。如果使用者不希望對方看到自己的狀态,可以選擇不響應<active/>節。
已經定義好的狀态有:Starting,Active,Composing,Paused,Inactive,Gone
消息類似于:
<message [email protected]/work [email protected] type="chat">
<body>Hi honey!</body>
<active xmlns="http://jabber.org/protocol/chatstates"/>
</message>
格式化消息
可以在message消息中加入XHTML用于富文本展示。
協定可以在[XEP-0071]中找到。
<message [email protected]/home [email protected] type="chat">
<body>I love this movie I saw last night, it's awesome!</body>
<html xmlns="http://jabber.org/protocol/xhtml-im">
<body xmlns="http://www.w3.org/1999/xhtml">
<p>
I <em>love</em>, this new movie I saw last night, it's <strong>awesome</strong>!
</p>
</body>
</html>
</message>
很容易注意到,消息中包含一個不包含格式的文本,以及XHTML格式的文本,是因為考慮到用戶端如果不支援HTML,也可以正常展示。
HTML節中不可以包含HEAD中的資訊,也不能包含腳本(安全性考慮)。
vCards
也就是虛拟名片,使用者在聊天時可以通過虛拟名片檢視相關資訊。
參考[XEP-0054]
虛拟名片服務通過發送IQ請求檢視和更新,但需要注意的是,更新虛拟名片時需要發送完整的資訊而不是隻有要更新的部分。
查詢請求:
<iq [email protected]/pool id="pw91nf84" [email protected] type="get">
<vCard xmlns="vcard-temp"/>
</iq>
傳回的消息:
<iq [email protected] id="pw91nf84" [email protected]/pool type="result">
<vCard xmlns="vcard-temp">
<N>
<GIVEN>Alice</GIVEN>
</N>
<URL>http://wonderland.lit/~alice/</URL>
<PHOTO>
<EXTVAL>http://www.cs.cmu.edu/~rgs/alice03a.gif</EXTVAL>
</PHOTO>
</vCard>
</iq>
更新名片:
<iq [email protected]/pda id="w0s1nd97" [email protected] type="set">
<vCard xmlns="vcard-temp">
<N>
<GIVEN>Alice</GIVEN>
</N>
<URL>http://wonderland.lit/~alice/</URL>
<PHOTO>
<EXTVAL>http://www.cs.cmu.edu/~rgs/alice03a.gif</EXTVAL>
</PHOTO>
<EMAIL>
<USERID>[email protected]</USERID>
</EMAIL>
</vCard>
</iq>
阻塞和過濾通信
類似于QQ可黑名單機制,可以對某人隐身,不看某人的消息或屏蔽某個域的消息等等;當然也包括對名單的修改。
該功能可支援的粒度非常細,參考标準[XEP-0016],[XEP-0191]。
更多消息擴充
- Extended Stanza Addressing[XEP-0033]
發送一條消息給多個接受者而不通過聊天室
- Advanced Message Processing[XEP-0079]
控制消息過期,避免消息被本地存儲以及延時投遞等
- Message Receipt[XEP-00184]
用戶端層面确認消息是否已經送達
- Message Archiving[XEP-0136]
在伺服器上存儲消息,而不是在用戶端機器上存儲
Service Discovery
我們需要知道系統中有哪些實體,以及該實體支援哪些服務,為了完成這些操作,引入了實體發現和服務發現的概念。
Items and Info
XMPP 服務發現協定[XEP-0030]定義了兩個基本的發現方法。首先發現disco#items,disco#info。
- 向伺服器發送發現實體請求
<iq [email protected]/home id="xl391n47" to="wonderland.lit" type="get">
<query xmlns="http://jabber.org/protocol/disco#items"/>
</iq>
- 伺服器響應發回實體清單
<iq from="wonderland.lit" id="xl391n47" [email protected]/home type="result">
<query xmlns="http://jabber.org/protocol/disco#items">
<item jid="conference.wonderland.lit"/>
<item jid="notify.wonderland.lit"/>
</query>
</iq>
- 查詢實體支援的功能
<iq [email protected]/home id="gq02kb71" to="conference.wonderland.lit" type="get">
<query xmlns="http://jabber.org/protocol/disco#info"/>
</iq>
- 傳回實體支援功能結果清單
<iq from="conference.wonderland.lit" id="gq02kb71" [email protected]/home type="result">
<query xmlns="http://jabber.org/protocol/disco#info">
<identity category="conference" type="text" name="Chatrooms"/>
<feature var="http://jabber.org/protocol/muc"/>
<feature var="jabber:iq:register"/>
<feature var="vcard-temp"/>
</query>
</iq>
Using Service Discovery with Servers and Services
服務發現同樣适用iq-get的disco#items/disco#info這兩個查詢操作,隻是将查詢是服務而非實體。
具體的查詢步驟較為複雜。
Using Service Discovery with Clients
Explicit Service Discovery
這種場景應用于使用者的花名冊向使用者傳回是否線上的資訊,這種判斷是否線上的資訊帶有Full JID,是以可以通過disco#info/disco#item來查詢。
但是這種查詢可能傳回某個Full JID攜帶的全部資訊,導緻資料量過大,是以引入了下面的方式。
Entity Capabilites: Service Discovery Shorthand
是對上面方法的改進,通過将實體支援的特性HASH為一組特征嗎,用戶端接收該特征碼後與本地存儲進行比較,如果已有該特征碼,則可以獲得支援的特性清單;如果用戶端沒有緩沖該特征碼,則重新發送disco#info消息擷取,并緩存。
采用此種方法可以節省響應的資源。
并且通過presence節就可以擷取用戶端支援的功能了。
Data Forms
類似于HTML的表單,有工作流的特征,可以實作使用者驗證碼輸入和确認等功能。
由[XEP-0004]定義。
Multi-Party Interaction
MCU 基礎
- 多人聊天最初被稱為groupchat,後來的疊代版本改進為Multi-User Chat(UMC)[XEP-0045]。
- MCU的基本思想是使用者加入到一個聊天室,而聊天室會多點傳播消息,聊天室起到消息反射器的作用
- 聊天室有如下特征
3.1 消息在所有的參與者中共享
3.2 所有的參與者都有一個room roster
3.3 參與者都使用其nickname辨別,而不是實際的JabberID
3.4 房間共享參與資訊
3.5 參與者不僅限于人,也可以是服務等
- 聊天室有其自己的JID,且該JID是伺服器的一個元件,是以具有不同的域,如伺服器的域稱為:wonderland.lit,元件的域為conference.wonderland.lit,實作MCU需要相應的元件,伺服器根據域的不同将消息路由到對應的元件上處理。
- 加入聊天流程
5.1 使用者發送presence消息
5.2 聊天室向成員廣播該presence
5.3 聊天室向使用者發送成員的presence
5.4 聊天室向使用者發送一些曆史消息好讓使用者參與讨論,消息數目可配置,且消息帶有時間戳
<delay xmlns="urn:xmpp:delay" stamp="2008-11-07T18:42:03Z"/>
5.5 之後的聊天消息不再攜帶時間戳
5.6 聊天室之間的消息往來,消息類型為groupchat
- 如果使用者向聊天室的成員發送消息,消息類型為chat,但消息實際上使用使用者發送給聊天室的,聊天室會改寫from/to字段為實際接受者的JID。
- 如果離開聊天室則會發送退席消息
<presence [email protected]/sleepspace [email protected]/Dormouse type="unavailable"/>
成員管理
群組中有多重角色,不同的角色擁有不同的權限,可以将使用者臨時踢出,或加入黑名單等。
具體有:outcast,visitor,participant,member,moderator,admin,owner。
另外房間也有不同的類型,有指定名單的,有臨時的,有隐藏的,有固定的等。
昵稱
使用者可以設定其在聊天室内的昵稱,參考In-Band Registration[XEP-0077]。
配置
多人聊天可以進行配置,有非常多的可配置項,列出如下。
配置項 | 作用 |
allowinvites | 是否允許普通成員邀請 |
changesubject | 是否非管理者能夠更改聊天室主題 |
enablelogging | 是否開啟記錄歸檔 |
getmemberlist | 是否能夠擷取成員清單 |
lang | 語言 |
maxuser | 最大參與者數量 |
membersonly | 是否僅會員可加入(适用于member類型聊天室) |
persistentroom | 房間是否為永久(所有成員退出也不會删除) |
presencebroadcast | 是否廣播presence消息,對大room有用 |
publicroom | 該room是否可被發現 |
roomadmin | 設定room管理者 |
roomdesc | 設定room描述 |
roomname | 設定room名稱 |
roomowner | 設定room 所有者 |
whois | 控制是否匿名等 |
資料傳輸
除了文字之外,MCU還可以傳輸地理資訊,檔案和進行遠端調用等。
Publish/Subscribe
實際上就是消息系統中的推模式,主要分三個步驟完成:首先訂閱一個主題;其次釋出一個消息;最後消息被推送到訂閱用戶端。[XEP-0060]
Subscriptions
訂閱者需要訂閱一個源,釋出者也将消息釋出到這個源。
流程:
- 使用者發送訂閱請求
- 伺服器響應該訂閱請求
如果成功則可以接收消息了,如果失敗,則有可能是要求更多的配置資訊
- 訂閱者對訂閱進行配置(可選)
- 訂閱者請求配置項
- 源傳回訂閱配置表單
- 使用者解除訂閱
- 伺服器傳回解除訂閱響應
Publishing and Receiving Notifications
主要分為兩個部分。
釋出方釋出消息到源;伺服器将源的消息通過message的形式投遞給訂閱方。
需要注意的是:釋出方隻負責将消息推送至源,投遞的邏輯由訂閱方的伺服器完成。
Payloads
使用者可以選擇是否在通知中攜帶payloads,如通知中包含頭像資訊時,隻需要metadata,無需具體的資料。
是否啟用payload可以通過配置項deliver_payloads來實作。
Items
是否儲存items也是可以配置的,可以通過persist_items配置。
儲存/不儲存資料的node分别稱為persistent nodes/transient node。
Discovering Nodes
nodes及其服務可以通過disco#info/disco#item來查詢
傳回的結果可以通過以上兩個指令進一步查詢,直到找到想要訂閱的node。
Personal Eventing
如果使用者想訂閱某個好友的動态,可以使用使用者的JID作為datanode,相應的簡化協定稱為PEP[XEP-0163](Personal Eventing Protocal)。
使用者可以應用PEP協定,在自己的Presence消息中包含自己的愛好資訊,伺服器收到該presence後,将會根據此愛好向使用者推送好友的動态。
包括User Tune,User Location,User Activity,User Mood,User Nickname,User Avatar等。
Design Decision
- 盡量不要修改XMPP的核心協定,而是應該試着通過新的namespace去擴充它
- 由于presence占用了大約90%的資料流量,需要控制presence節中的資料
- 需要盡量簡化XMPP用戶端的設計,尤其是需要減少對伺服器端的壓力
- 盡量重用已有協定,而不是重新設計一個