基於XMPP的即時通訊系統的建立(四)— 協議詳解

檀志文發表於2017-12-14

Presence(http://www.cnblogs.com/jiyuqi/tag/xmpp/)

在XMPP協議中,我們使用presence來獲取使用者是否已經上線以及是否可以通訊的狀態。

為了能夠知道自己聯絡人的狀態以及讓聯絡人知道自己的狀態,使用者上線後需要訂閱聯絡人的狀態,聯絡人也同樣需要訂閱使用者的狀態。

通過下面的訊息訂閱聯絡人的狀態:

當聯絡人接收/拒絕訂閱時,會傳送訊息的訊息體(sucribed/unsubscribe)迴應。

通常客戶端是自動迴應這些訊息的,當我們訂閱了聯絡人的狀態之後,也會受到聯絡人的狀態變更資訊。

還可以通過嵌入(chat/away/xa/dnd)和元素表示更加豐富的資訊。

需要注意的是presence節是佔用頻寬最多的節,任何對該節內容的擴充套件都需要慎重。另外,presence還提供priority屬性用來標識資源的優先順序(-127-128),負數優先順序的資源將無法接收訊息,除非顯式指定,這個特性通常是由伺服器實現的。

另外,我們可以通過傳送directed presence到其他使用者,來避免訂閱對方資訊,非常適合應用於網路而上的簡短交流。

可以通過傳送傳送下線通知,伺服器會完成訊息儲存,聯絡人通知等一系列操作。

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

錯誤的資訊節,反饋錯誤資訊

延時投遞

如果使用者不線上,則訊息被快取,使用者登入後,訊息推送給使用者,並且攜帶訊息原始產生時間,用於客戶端對訊息進行排序。

Chat session

chat session用於使用者頻繁交流的情況,這類似於現實情況中的聊天,其建立過程為:雙方使用者在訊息互動中知道了對方的Full JID,因此可以直接通訊,響應的機制稱為chat session。

狀態通知類似於QQ的“正在輸入”功能,讓互動雙方瞭解即時狀態。該功能擴充套件由XEP-0085定義。如果使用者不希望對方看到自己的狀態,可以選擇不響應節。已經定義好的狀態有:Starting,Active,Composing,Paused,Inactive,Gone

格式化訊息

可以在message訊息中加入XHTML用於富文字展示。

協議可以在[XEP-0071]中找到。

vCards

也就是虛擬名片,使用者在聊天時可以通過虛擬名片檢視相關資訊。

阻塞和過濾通訊

類似於QQ可黑名單機制,可以對某人隱身,不看某人的訊息或遮蔽某個域的訊息等等;當然也包括對名單的修改。

更多訊息擴充套件

Extended Stanza Addressing[XEP-0033]

傳送一條訊息給多個接受者而不通過聊天室

Advanced Message Processing[XEP-0079]

控制訊息過期,避免訊息被本地儲存以及延時投遞等

Message Receipt[XEP-00184]

客戶端層面確認訊息是否已經送達

Message Archiving[XEP-0136]

在伺服器上儲存訊息,而不是在客戶端機器上儲存

Service Discovery

我們需要知道系統中有哪些實體,以及該實體支援哪些服務,為了完成這些操作,引入了實體發現和服務發現的概念。

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 InteractionMCU 基礎多人聊天最初被稱為groupchat,後來的迭代版本改進為Multi-User Chat(UMC)[XEP-0045]。MCU的基本思想是使用者加入到一個聊天室,而聊天室會組播訊息,聊天室起到訊息反射器的作用聊天室有如下特徵3.1    訊息在所有的參與者中共享3.2    所有的參與者都有一個room roster3.3    參與者都使用其nickname標識,而不是實際的JabberID3.4    房間共享參與資訊3.5    參與者不僅限於人,也可以是服務等聊天室有其自己的JID,且該JID是伺服器的一個元件,因此具有不同的域,如伺服器的域稱為:wonderland.lit,元件的域為conference.wonderland.lit,實現MCU需要相應的元件,伺服器根據域的不同將訊息路由到對應的元件上處理。加入聊天流程5.1    使用者傳送presence訊息5.2    聊天室向成員廣播該presence5.3    聊天室向使用者傳送成員的presence5.4    聊天室向使用者傳送一些歷史訊息好讓使用者參與討論,訊息數目可配置,且訊息帶有時間戳5.5    之後的聊天訊息不再攜帶時間戳5.6    聊天室之間的訊息往來,訊息型別為groupchat如果使用者向聊天室的成員傳送訊息,訊息型別為chat,但訊息實際上使用使用者傳送給聊天室的,聊天室會改寫from/to欄位為實際接受者的JID。如果離開聊天室則會傳送退席訊息

成員管理

群組中有多重角色,不同的角色擁有不同的許可權,可以將使用者臨時踢出,或加入黑名單等。

具體有: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客戶端的設計,尤其是需要減少對伺服器端的壓力

儘量重用已有協議,而不是重新設計一個

相關文章