XMPP 簡單介紹
本小節將簡要介紹 XMPP,它的起源。以及為何它是一個適合實時 web 通信的協議。您將檢查 XMPP 通信設置的組件,並查看展示這些組件怎樣使用的演示樣例。
XMPP 是一組基於 XML 的技術。用於實時應用程序。最初。XMPP 作為一個框架開發。目標是支持企業環境內的即時消息傳遞和聯機狀態應用程序。當時的即時消息傳遞網絡是私有的,很不適合企業使用。比如。AOL Instant Messenger 不能針對公司內的安全通信進行調整。雖然存在一些商業解決方式,但它們固定的特性集通常不能進行調整,以滿足組織的特殊需求。XMPP,當時名為 Jabber,同意組織構建自己的定制工具來促進實時通信。並同意安裝現成的第三方解決方式。
XMPP 是一個分散型通信網絡,這意味着,僅僅要網絡基礎設施同意,不論什么 XMPP 用戶都能夠向其它不論什么 XMPP 用戶傳遞消息。
多個 XMPP server也能夠通過一個專門的 “server-server” 協議相互通信。提供了創建分散型社交網絡和協作框架的有趣可能性。但這個主題已超出了本教程的討論范圍。
顧名思義,XMPP 可用於滿足廣泛的、對時間敏感的特性要求。實際上。Google Wave。一個大型多用戶協作環境。將 XMPP 作為其聯合協議的基礎。雖然 XMPP 的出現是為了滿足 “個人-個人” 即時消息傳遞的要求。但它全然不必局限於此任務。
要促進消息傳遞,每一個 XMPP client用戶必須擁有一個全局惟一標識符。基於歷史原因。這些標識符稱為 Jabber IDs。或稱為 JIDs。
鑒於這個協議的分布式特征。重要的是 JID 應包括聯系用戶所需的全部信息:不存在將用戶鏈接到他們連接到的server的中央知識庫。
JID 的結構類似於電子郵件地址(但不要求 JID 同一時候也是有效的電子郵件收件人)。
client和server節點。我將它們統稱為 XMPP 實體,都擁有 JIDs。SomeCorp 公司的員工 John Doe 可能擁有 JIDJohn.Doe@somecorp.com
。
這里,somecorp.com
是 SomeCorp 公司的 XMPP server的地址。John.Doe
是 John Doe 的username。
JIDs 還擁有連接到它們的資源。
這同意在一個 XMPP 實體標識符之外進一步處理細粒度。比如,雖然上面的演示樣例整體上可以表示 John Doe。但 John.Doe@somecorp.com/Work
可以用於將數據發送到與他的工作相關的工具。
這些資源能夠採用隨意用戶定義的名稱。一個 XMPP 實體能夠擁有隨意數量的資源。除了能夠是上下文依賴的外,它們還能夠綁定到設備、工具或工作站。對於您的 Pingstream 演示樣例,web 網站的每一個訪問者都將作為同一個用戶登錄 XMPP server。但他們擁有不同的資源。
使用 XMPP 的實時消息傳遞系統包括三大通信類別:
- 消息傳遞。當中數據在有關各方之間傳輸;
- 聯機狀態,它同意用戶廣播其在線狀態和可用性;
- 信息/查詢請求。它同意 XMPP 實體發起請求並從還有一個實體接收響應。
這些類別是互補的。比如。假設用戶或實體離線(雖然在很多用例中,理想的狀態是server在用戶返回之前一直持實用戶的消息),則沒有將數據發送給用戶或發起一個實體的信息/查詢請求的點。
這些消息中的每一條都將通過一個完整的 XML節 傳遞 — XML 節是以 XML 表達的獨立信息項。
這三種類型的 XMPP 節都擁有下面公共屬性:
from
:源 XMPP 實體的 JID;to
:目標接收者的 JID;id
:這次對話的可選標識符;type
:節的可選子類型。xml:lang
:假設內容是人們可讀的,則為消息語言的描寫敘述。
基於 XMPP 的傳輸數據發生在一些 XML 流上。默認在port 5222 上操作。這些 XML 流實際上是兩個完整的 XML 文檔。每一個文檔相應一個通信方向。
一旦會話建立。stream
元素將打開。
這個元素將封裝整個通信文檔。
然后。一些節被注入這個文檔的第二層。
最后,一旦通信結束,stream
元素將關閉,形成一個完整的文檔。
比如。清單 1 展示了一個 stream
元素,它建立了從client到server的通信。
清單 1. 建立從client到server的通信的 stream 標記
<stream:stream from="[server]" id="[unique ID over conversation]" xmlns="jabber:client" xmlns:stream="http://etherx.jabber.org/streams" version="1.0"> |
一旦通信建立,client就能使用 message
元素將消息發送到還有一個用戶,message 元素包括下面隨意子元素:
subject
:一個可讀的字符串。表示消息主題。body
:一個可讀的字符串,表示消息體。假設每一個消息體標記都擁有一個不同的
xml:lang
值,那么能夠包括多個消息體標記。(
xml:lang
是惟一可能的屬性。)thread
:一個惟一標識符。表示一個消息線程。client軟件能夠使用這個子元素將相關消息串聯在一起。
可是,消息也能夠很easy,如 清單 2 所看到的:
<message from="sendinguser@somedomain" to="recipient@somedomain" xml:lang='en'> <body> Body of message </body> </message> |
對於提供實時 web 界面而言,消息節是最實用的節。
“公布-訂閱” 模型 — 在實時 web 應用程序中使用消息來數據傳輸的一種替代方法 — 將稍后介紹。
信息/查詢節擁有廣泛的功能。一個樣例就是 “公布-訂閱” 模型,在該模型中,公布者通知server某個特定資源進行了更新。server則通知已選擇訂閱這些通知並擁有適當授權的全部 XMPP 用戶。
來自公布者的一系列項目被編碼為一些節。格式為基於 XML 的 Atom 公布格式。
每一個項目都包括在一個 item
元素內,然后合並到一個 pubsub
元素中。最后成為一個信息/查詢節。在 清單 3(選自 XMPP 公布-訂閱規范)中,Shakespeare's Hamlet(JID 為 hamlet@denmark.lit/blogbot
)用他著名的獨白公布一個更新到 pubsub.shakespeare.lit
pubsub 更新節點:
清單 3. 對 pubsub.shakespeare.lit
pubsub 更新節點的更新
<iq type="set" from="hamlet@denmark.lit/blogbot" to="pubsub.shakespeare.lit" id="pub1"> <pubsub xmlns="http://jabber.org/protocol/pubsub"> <publish node="princely_musings"> <item> <entry xmlns="http://www.w3.org/2005/Atom"> <title>Soliloquy</title> <summary> To be, or not to be: that is the question: Whether 'tis nobler in the mind to suffer The slings and arrows of outrageous fortune, Or to take arms against a sea of troubles, And by opposing end them? </summary> <link rel="alternate" type="text/html" href="http://denmark.lit/2003/12/13/atom03"/> <id>tag:denmark.lit,2003:entry-32397</id> <published>2003-12-13T18:30:02Z</published> <updated>2003-12-13T18:30:02Z</updated> </entry> </item> </publish> </pubsub> </iq> |
信息/查詢節也用於請求一個特定 XMPP 實體的有關信息。比如,在 清單 4 中的節中,boreduser@somewhere
正在查找friendlyuser@somewhereelse
擁有的公共項目。
清單 4. 用戶查找由 friendlyuser@somewhereelse
擁有的公共項目
<iq type="get" from="boreduser@somewhere" to="friendlyuser@somewhereelse" id="publicStuff"> <query xmlns="http://jabber.org/protocol/disco#items"/> </iq> |
反過來,friendlyuser@somewhereelse
使用一列可被訂閱到使用 “公布-訂閱” 的項目進行響應,如 清單 5 所看到的:
<iq type="result" from="friendlyuser@somewhereelse" to="boreduser@somewhere" id="publicStuff"> <query xmlns="http://jabber.org/protocol/disco#items"> <item jid="stuff.to.do" name="Things to do"/> <item jid="stuff.to.not.do" name="Things to avoid doing"/> </query> </iq> |
在 清單 5 中的信息/查詢節中的每一個返回項目都擁有一個能夠訂閱到的 JID。信息/查詢還同意超出本教程范圍的廣泛的server信息請求。
它們中的很多在針對多server環境的 web 應用程序上下文中實用,或者作為復雜的分散型協作框架的基礎。
聯機狀態信息包括在一個聯機狀態(presence)節中。假設 type
屬性省略。那么 XMPP client應用程序假定用戶在線且可用。
否則,type
可設置為 unavailable
,或者特定於 pubsub 的值:subscribe
、subscribed
、unsubscribe
和unsubscribed
。它也能夠是針對還有一個用戶的聯機狀態信息的一個錯誤或探針。
一個聯機狀態節能夠包括下面子元素:
show
:一個機器可讀的值。表示要顯示的在線狀態的整體類別。這能夠是away
(臨時離開)、chat
(可用且有興趣交流)、dnd
(請勿打攪)、或xa
(長時間離開)。status
:一個可讀的 show 值。該值為用戶可定義的字符串。
priority
:一個位於 -128 到 127 之間的值。定義消息路由到用戶的優先順序。假設值為負數,用戶的消息將被扣留。
比如。清單 6 中的 boreduser@somewhere
能夠用這個節來表明聊天意願:
<presence xml:lang="en"> <show>chat</show> <status>Bored out of my mind</status> <priority>1</priority> </presence> |
注意 from
屬性此處省略。
還有一個用戶 friendlyuser@somewhereelse
能夠通過發送 清單 7 中的節來探測 boreduser@somewhere
的狀態:
<presence type="probe" from="friendlyuser@somewhereelse" to="boreduser@somewhere"/> Boreduser@somewhere's server would then respond with a tailored presence response: <presence xml:lang="en" from="boreduser@somewhere" to="friendlyuser@somewhereelse"> <show>chat</show> <status>Bored out of my mind</status> <priority>1</priority> </presence> |
這些聯機狀態值源自 “個人-個人” 消息傳遞軟件。show
元素的值 — 通經常使用於確定將向其它用戶顯示的狀態圖標 — 在聊天應用程序之外怎樣使用如今還不清楚。
狀態值可能會在微博工具中找到用武之地。比如,Google Talk(一個 XMPP 聊天服務)中的用戶狀態字段的更改能夠被導入為 Google Buzz 中的微博條目。
還有一種可能性就是將狀態值用作每用戶應用程序狀態數據的攜帶者。
雖然此規范將狀態定義為可讀,但沒有什么能夠阻止您在那里存儲隨意字符串來滿足您的要求。
對於某些應用程序而言。它能夠不是可讀的,或者。它能夠攜帶微格式形態的數據負載。
您能夠為一個 XMPP 實體擁有的每一個資源獨立設置聯機狀態信息。以便訪問和接收連接到一個應用程序中的單個用戶的全部工具和上下文的數據僅僅需一個用戶帳戶。每一個資源都能夠被分配一個獨立的優先級;XMPP server將首先嘗試將消息傳遞給優先級較高的資源。
要通過使用 JavaScript 的 XMPP 進行通信的 web 應用程序必須符合一些特殊要求。出於安全考慮。不同意 JavaScript 從 web 頁面的域與不同域上的多個server通信。假設您的 web 應用程序界面被托管在 application.mydomain.com
。全部 XMPP 通信也必須發生在 application.mydomain.com
。
防火牆是還有一個問題所在。理想情況下,假設您將 XMPP 用作您的 web 界面的實時元素的基礎,那么您希望它對防火牆后面的用戶有效。可是。公司防火牆通常僅僅對少數幾個協議開放幾個port,以便同意 web 數據、電子郵件和類似的通信通過。默認情況下,XMPP 使用port 5222,這非常可能是公司防火牆阻止的port。
如果您知道您的用戶前面的防火牆在port 80 上同意 HTTP(這是用於訪問 web 的默認協議和port)。理想情況是您的 XMPP 通信可以越過該port上的 HTTP。可是,HTTP 的設計並不針對持續連接。
web 的架構不同於實時數據所需的通信架構。
以下我們看看 Bidirectional-streams Over Synchronous HTTP (BOSH) 的標准,該標准為雙向同步數據提供一個模擬層。
借助這個標准,能夠與一個 XMPP server建立一個較長的 HTTP 連接(時長一分鍾或兩分鍾)。
假設新數據在那個期間到達,則 HTTP 請求返回數據並關閉。否則,該請求僅僅是失效。無論是哪種情況,一旦一個請求關閉,還有一個請求將又一次建立。雖然結果是對一個 web server的一系列反復連接,但它是一個比 Ajax 輪詢更有效的數量級。特別是由於連接到的是一個專業server而不是直接連接到 web 應用程序。
BOSH 上的 XMPP 同意 web 應用程序通過一個原生連接持續與 XMPP server通信。client通過port 80 上的 HTTP 上的一個標准 URL 連接。然后,web server將這個連接代理到由 XMPP server操作的一個不同port — 一般是 7070 — 上的 HTTP URL。這樣,不管何時數據被發送到 XMPP server,web 應用程序僅僅需使用一些資源,而 web client能夠使用通常支持的 web 標准從防火牆后操作。維持 BOSH 的較長 HTTP 輪詢的開銷主要由 XMPP server而不是 web server或 web 應用程序承擔。
web server和 XMPP server都不會受到與使用 JavaScript 進行通信一樣的域限制。正是由於這一點。消息才可以被發送到其它 XMPP server和client。
如今,您理解了 XMPP 怎樣適合實時 web,能夠下載並設置它。以便開始創建這個 Pingstream 應用程序。