天天看點

基于XMPP的即時通信系統的建立(四)— 協定詳解

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

消息發送流程

  1. user1向user2發送消息,user1位于domain1,user2位于domain2
  2. user1的client1向server1發送消息節,server1設定from屬性
  3. server1投遞消息給server2(直接通信)
  4. server2收到消息并檢查user2是否線上并投遞
  5. 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 基礎

  1. 多人聊天最初被稱為groupchat,後來的疊代版本改進為Multi-User Chat(UMC)[XEP-0045]。
  2. MCU的基本思想是使用者加入到一個聊天室,而聊天室會多點傳播消息,聊天室起到消息反射器的作用
  3. 聊天室有如下特征

3.1    消息在所有的參與者中共享

3.2    所有的參與者都有一個room roster

3.3    參與者都使用其nickname辨別,而不是實際的JabberID

3.4    房間共享參與資訊

3.5    參與者不僅限于人,也可以是服務等

  1. 聊天室有其自己的JID,且該JID是伺服器的一個元件,是以具有不同的域,如伺服器的域稱為:wonderland.lit,元件的域為conference.wonderland.lit,實作MCU需要相應的元件,伺服器根據域的不同将消息路由到對應的元件上處理。
  2. 加入聊天流程

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

  1. 如果使用者向聊天室的成員發送消息,消息類型為chat,但消息實際上使用使用者發送給聊天室的,聊天室會改寫from/to字段為實際接受者的JID。
  2. 如果離開聊天室則會發送退席消息

<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

訂閱者需要訂閱一個源,釋出者也将消息釋出到這個源。

流程:

  1. 使用者發送訂閱請求
  2. 伺服器響應該訂閱請求

如果成功則可以接收消息了,如果失敗,則有可能是要求更多的配置資訊

  1. 訂閱者對訂閱進行配置(可選)
  2. 訂閱者請求配置項
  3. 源傳回訂閱配置表單
  4. 使用者解除訂閱
  5. 伺服器傳回解除訂閱響應

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

  1. 盡量不要修改XMPP的核心協定,而是應該試着通過新的namespace去擴充它
  2. 由于presence占用了大約90%的資料流量,需要控制presence節中的資料
  3. 需要盡量簡化XMPP用戶端的設計,尤其是需要減少對伺服器端的壓力
  4. 盡量重用已有協定,而不是重新設計一個