Ben Werdmuller 是一位 Web 策劃師和開發人員,他專一於開放源碼平臺。他是開源社交網絡框架 Elgg 的共同創始人和技術帶頭人。Ben 的博客 http://benwerd.com/。javascript
簡介: 實時 web 應用程序是聯網的應用程序,帶有基於 web 的用戶界面,可以及時顯示剛剛發佈的 Internet 信息。這樣的應用程序示例包括社會新聞聚合器和監控工具,它們可以使用來自外部源的數據持續更新。在本教程中,您將建立一個小型通知工具 Pingstream,它使用 PHP 和 JavaScript 經過 Extensible Messaging and Presence Protocol (XMPP) 進行通訊,XMPP 是一組設計用於支持聯機狀態和實時通訊功能的 XML 技術。php
本教程將向您介紹實時 web,並詳細介紹之因此要構建實時 web 應用程序的幾個緣由。您將學到一些技術,這些技術將幫助您建立響應及時、持續更新的 web 應用程序,這些應用程序既能保護服務器資源,又能提供良好的用戶體驗。html
DOM: 文檔對象模型前端
HTML: 超文本標記語言java
HTTP: 超文本傳輸協議node
REST: 具象狀態傳輸mysql
RSS: 真正簡單聚合jquery
URL: 統一資源定位符web
XML: 可擴展標記語言ajax
實時 web 應用程序容許用戶在信息發佈時及時接收通知,無需手動檢查原始源獲取更新。經過 Twitter 和 Friendfeed 這樣的社交通知工具,Google Wave 這樣的基於 web 的協做工具,以及 Meebo 這樣的基於 web 的聊天客戶端,實時 web 應用程序逐漸流行起來。
Extensible Messaging and Presence Protocol (XMPP) 是一組基於 XML 的技術,用於實時應用程序,定義爲持續更新以響應新數據或更改數據的聯網應用程序。它最初做爲一個框架研發,以支持企業環境內的實時消息傳遞和聯機狀態(presence)應用程序。
在本教程中,您將構建一個簡單的工具 Pingstream,它在 RSS 提要更新發布時使用它們持續更新自身(參見 下載 獲取 Pingstream 源代碼)。在此過程當中,您將:
瞭解 XMPP 爲什麼特別適合 web 應用程序;
瞭解 XMPP 通訊的組件;
安裝和配置 Openfire XMPP 服務器;
使用 PHP 和 XMPPHP 庫鏈接到 XMPP 服務器;
檢查並經過 XMPP 傳輸 RSS 提要中的新項目;
藉助 Bidirectional-streams Over Synchronous HTTP (BOSH),使用 Strophe 和 jQuery 經過 HTTP 鏈接到 XMPP 服務器;
在 web 頁面中顯示 XMPP 通知。
本教程假定您比較熟悉使用 PHP 開發 web 應用程序,但也會涉及一些高級編程方法。您還應該擁有必定的 HTML 和 JavaScript 經驗。擁有 jQuery JavaScript 框架經驗可能會有所幫助。但本教程不要求熟悉 XMPP 或相似的技術。
要跟隨本教程操做,必須安裝和運行如下服務器軟件:
PHP 5.2 或更高版本
Apache HTTP Server
MySQL
在本教程中,您還將下載和安裝如下軟件和庫:
Openfire
jQuery
Strophe
XMPPHP
Last RSS
您可能會發現 MySQL 服務器工具 phpMyAdmin 可以派上用場。若是您使用一臺桌面機器來本地測試您的實時 web 應用程序,您可能會發現 XAMPP 對於管理一個測試 web 服務器基礎設施的安裝和運行頗有用。
實時 web 簡介
在本小節中,您將瞭解什麼是實時 web 應用程序,您爲什麼可能須要構建這樣的應用程序,以及它們與典型的現代 web 應用程序模型的區別何在。
實時 web 並不實時
持續更新的 web 應用程序 這個詞彙比實時 web 應用程序 更貼切。在計算機科學中,硬 和軟 實時系統都必須知足操做期限的要求。當任務不能在其分配時間內完成時,硬實時系統將失敗。實時 web 應用程序與軟實時系統更類似,在軟實時系統中,某個功能的延遲並不會致使系統失敗(但可能會下降性能)。但您不該對實時 web 應用程序分配嚴格的任務計劃。目前,web 並非適合時間關鍵型應用程序的平臺。
應用程序是幫助用戶執行任務的專門軟件,其特徵是:從用戶或其餘源接收輸入,而後提供可讀的輸出。應用程序也可能動態響應 — 以可視或編程方式 — 自動接收的輸入數據中的變化。例如,當包含特定關鍵字的新聞出如今一個新聞監控應用程序鏈接到的新聞專線中時,該應用程序可能會通知用戶。
因爲 web 的起源是做爲一個文檔服務平臺,所以它沒有針對應用程序優化。HTML 很是適合表示和超連接文本內容,但不適合建立動態界面。web 應用程序可以接收和響應用戶輸入,這要歸功於 PHP 這樣的服務器端腳本語言與表單和 JavaScript 這樣的 web 輸入技術的結合。可是,要建立自動 更新的界面,您必須克服一些障礙。這些障礙比較難以克服,由於沒有任何 web 技術在研發時考慮到這個功能。相比之下(以新聞監控爲例),桌名軟件無需刷新其界面就可以將通知發送給用戶;桌面軟件能夠持續更新自身。相反,web 則侷限於基於頁面的模型。
然而,基於 web 的實時應用程序仍是能夠實現的,並且它們的好處顯而易見。這樣的應用程序包括企業聊天工具、聯網的實時文檔協做工具、以及搜索界面,它們可以及時顯示新發布的內容。
一般,web 應用程序經過使用 Asynchronous JavaScript and XML (Ajax) 工具來模擬持續更新的界面。在這個模型中,應用程序的 web 頁面中包含 JavaScript,它在後臺反覆請求一個服務器回撥。儘管 Ajax 應用程序的響應性在不少狀況下都夠用,但這種技術仍是有一些缺陷。
Ajax 不能容忍不穩定的 Internet 鏈接:一次臨時掉線可能會致使整個界面失敗。它在服務器負載方面也效率低下。假設您的後臺 Ajax 輪詢函數每 10 秒檢查一次服務器。每一次都將創建一個新的 HTTP 鏈接,包括初始化服務請求所需的資源,即便沒有新數據能夠向用戶顯示。結果是應用程序沒必要要地使用過多的處理器時間和帶寬。
基於 XML 的技術向 web 應用程序提供了巨大的優點。XML 解析器如今是大多數環境的一個標準組成部分;無需其餘軟件就能支持以適當的格式讀寫數據。XML 是自我描述型語言;使用它的文檔不須要外部架構。最後,正如 web 是獨立於平臺的同樣,XML 做爲一種技術支持在不一樣平臺間互操做。所以,開發人員能夠將精力集中於特定於他們的應用程序的邏輯。
web 基於 HTML、Cascading Style Sheets (CSS) 和 JavaScript 等可互操做的免費開源標準。若是針對 web 上的實時通訊的新標準出現,那麼該標準也應是免費、開源和可互操做的。基於 XML 的 XMPP 知足這些標準。在本教程中,您將使用 XMPP 來構建一個客戶端庫,它經過標準方法(好比一個 web hook)來接收輸入,並將適當的數據實時中繼到用戶。
本小節將簡要介紹 XMPP,它的起源,以及爲什麼它是一個適合實時 web 通訊的協議。您將檢查 XMPP 通訊設置的組件,並查看展現這些組件如何使用的示例。
XMPP 是一組基於 XML 的技術,用於實時應用程序。最初,XMPP 做爲一個框架開發,目標是支持企業環境內的即時消息傳遞和聯機狀態應用程序。當時的即時消息傳遞網絡是私有的,很是不適合企業使用。例如,AOL Instant Messenger 不能針對公司內的安全通訊進行調整。儘管存在一些商業解決方案,但它們固定的特性集一般不能進行調整,以知足組織的特殊需求。XMPP,當時名爲 Jabber,容許組織構建本身的定製工具來促進實時通訊,並容許安裝現成的第三方解決方案。
XMPP 是一個分散型通訊網絡,這意味着,只要網絡基礎設施容許,任何 XMPP 用戶均可以向其餘任何 XMPP 用戶傳遞消息。多個 XMPP 服務器也能夠經過一個專門的 「服務器-服務器」 協議相互通訊,提供了建立分散型社交網絡和協做框架的有趣可能性,但這個主題已超出了本教程的討論範圍。
顧名思義,XMPP 可用於知足普遍的、對時間敏感的特性要求。實際上,Google Wave,一個大型多用戶協做環境,將 XMPP 做爲其聯合協議的基礎。儘管 XMPP 的出現是爲了知足 「我的-我的」 即時消息傳遞的要求,但它徹底沒必要侷限於此任務。
要促進消息傳遞,每一個 XMPP 客戶端用戶必須擁有一個全局唯一標識符。基於歷史緣由,這些標識符稱爲 Jabber IDs,或稱爲 JIDs。鑑於這個協議的分佈式特徵,重要的是 JID 應包含聯繫用戶所需的全部信息:不存在將用戶連接到他們鏈接到的服務器的中央知識庫。JID 的結構相似於電子郵件地址(但不要求 JID 同時也是有效的電子郵件收件人)。
客戶端和服務器節點,我將它們統稱爲 XMPP 實體,都擁有 JIDs。SomeCorp 公司的員工 John Doe 可能擁有 JID John.Doe@somecorp.com。這裏,somecorp.com 是 SomeCorp 公司的 XMPP 服務器的地址,John.Doe 是 John Doe 的用戶名。
JIDs 還擁有鏈接到它們的資源。這容許在一個 XMPP 實體標識符以外進一步處理細粒度;例如,儘管上面的示例整體上可以表示 John Doe,但 John.Doe@somecorp.com/Work 能夠用於將數據發送到與他的工做相關的工具。
這些資源能夠採用任意用戶定義的名稱,一個 XMPP 實體能夠擁有任意數量的資源。除了能夠是上下文依賴的外,它們還能夠綁定到設備、工具或工做站。對於您的 Pingstream 示例,web 站點的每一個訪問者都將做爲同一個用戶登陸 XMPP 服務器,但他們擁有不一樣的資源。
使用 XMPP 的實時消息傳遞系統包含三大通訊類別:
消息傳遞,其中數據在有關各方之間傳輸;
聯機狀態,它容許用戶廣播其在線狀態和可用性;
信息/查詢請求,它容許 XMPP 實體發起請求並從另外一個實體接收響應。
這些類別是互補的。例如,若是用戶或實體離線(儘管在許多用例中,理想的狀態是服務器在用戶返回以前一直持有用戶的消息),則沒有將數據發送給用戶或發起一個實體的信息/查詢請求的點。這些消息中的每一條都將經過一個完整的 XML 節 傳遞 — XML 節是以 XML 表達的獨立信息項。
這三種類型的 XMPP 節都擁有如下公共屬性:
from:源 XMPP 實體的 JID;
to:目標接收者的 JID;
id:此次對話的可選標識符;
type:節的可選子類型;
xml:lang:若是內容是人們可讀的,則爲消息語言的描述。
基於 XMPP 的數據傳輸發生在一些 XML 流上,默認在端口 5222 上操做。這些 XML 流其實是兩個完整的 XML 文檔,每一個文檔對應一個通訊方向。一旦會話創建,stream 元素將打開。這個元素將封裝整個通訊文檔。而後,一些節被注入這個文檔的第二層。最後,一旦通訊結束,stream 元素將關閉,造成一個完整的文檔。
例如,清單 1 展現了一個 stream 元素,它創建了從客戶端到服務器的通訊。
清單 1. 創建從客戶端到服務器的通訊的 stream 標記
<stream:stream from="[server]" id="[unique ID over conversation]" xmlns="jabber:client" xmlns:stream="http://etherx.jabber.org/streams" version="1.0"> |
一旦通訊創建,客戶端就能使用 message 元素將消息發送到另外一個用戶,message 元素包含如下任意子元素:
subject:一個可讀的字符串,表示消息主題。
body:一個可讀的字符串,表示消息體。若是每一個消息體標記都擁有一個不一樣的 xml:lang 值,那麼能夠包含多個消息體標記。(xml:lang 是唯一可能的屬性。)
thread:一個唯一標識符,表示一個消息線程。客戶端軟件可使用這個子元素將相關消息串聯在一塊兒。
可是,消息也能夠很是簡單,如 清單 2 所示:
<message from="sendinguser@somedomain" to="recipient@somedomain" xml:lang='en'> <body> Body of message </body> </message> |
對於提供實時 web 界面而言,消息節是最有用的節。「發佈-訂閱」 模型 — 在實時 web 應用程序中使用消息來傳輸數據的一種替代方法 — 將稍後介紹。
信息/查詢節擁有普遍的功能。一個例子就是 「發佈-訂閱」 模型,在該模型中,發佈者通知服務器某個特定資源進行了更新,服務器則通知已選擇訂閱這些通知並擁有適當受權的全部 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" target="_blank" rel="nofollow"> <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。信息/查詢還容許超出本教程範圍的普遍的服務器信息請求。它們中的許多在針對多服務器環境的 web 應用程序上下文中有用,或者做爲複雜的分散型協做框架的基礎。
聯機狀態信息包含在一個聯機狀態(presence)節中。若是 type 屬性省略,那麼 XMPP 客戶端應用程序假定用戶在線且可用。不然,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 服務器將首先嚐試將消息傳遞給優先級較高的資源。
要經過使用 JavaScript 的 XMPP 進行通訊的 web 應用程序必須符合一些特殊要求。出於安全考慮,不容許 JavaScript 從 web 頁面的域與不一樣域上的多個服務器通訊。若是您的 web 應用程序界面被託管在 application.mydomain.com,全部 XMPP 通訊也必須發生在 application.mydomain.com。
防火牆是另外一個問題所在。理想狀況下,若是您將 XMPP 用做您的 web 界面的實時元素的基礎,那麼您但願它對防火牆後面的用戶有效。可是,公司防火牆一般只對少數幾個協議開放幾個端口,以便容許 web 數據、電子郵件和相似的通訊經過。默認狀況下,XMPP 使用端口 5222,這極可能是公司防火牆阻止的端口。
假設您知道您的用戶前面的防火牆在端口 80 上容許 HTTP(這是用於訪問 web 的默認協議和端口)。理想狀況是您的 XMPP 通訊可以越過該端口上的 HTTP。可是,HTTP 的設計並不針對持續鏈接。web 的架構不一樣於實時數據所需的通訊架構。
下面咱們看看 Bidirectional-streams Over Synchronous HTTP (BOSH) 的標準,該標準爲雙向同步數據提供一個模擬層。藉助這個標準,能夠與一個 XMPP 服務器創建一個較長的 HTTP 鏈接(時長一分鐘或兩分鐘)。若是新數據在那個期間到達,則 HTTP 請求返回數據並關閉;不然,該請求只是失效。無論是哪一種狀況,一旦一個請求關閉,另外一個請求將從新創建。儘管結果是對一個 web 服務器的一系列重複鏈接,但它是一個比 Ajax 輪詢更有效的數量級,特別是由於鏈接到的是一個專業服務器而不是直接鏈接到 web 應用程序。
BOSH 上的 XMPP 容許 web 應用程序經過一個原生鏈接持續與 XMPP 服務器通訊。客戶端經過端口 80 上的 HTTP 上的一個標準 URL 鏈接。而後,web 服務器將這個鏈接代理到由 XMPP 服務器操做的一個不一樣端口 — 一般是 7070 — 上的 HTTP URL。這樣,不管什麼時候數據被髮送到 XMPP 服務器,web 應用程序只需使用一些資源,而 web 客戶端可使用一般支持的 web 標準從防火牆後操做。維持 BOSH 的較長 HTTP 輪詢的開銷主要由 XMPP 服務器而不是 web 服務器或 web 應用程序承擔。web 服務器和 XMPP 服務器都不會受到與使用 JavaScript 進行通訊同樣的域限制,正是由於這一點,消息纔可以被髮送到其餘 XMPP 服務器和客戶端。
如今,您理解了 XMPP 如何適合實時 web,能夠下載並設置它,以便開始建立這個 Pingstream 應用程序。
在本小節中,您將安裝 Openfire XMPP 服務器並配置它來支持您的實時 web 應用程序。
有兩個領先的開源 XMPP 服務器能夠免費下載。它們都應用普遍並經過 GNU Public License version 2 許可,每一個服務器都有本身的優點和缺點:
l ejabberd:ejabberd 中的 e 指的是 Erlang,一種軟實時編程語言。這一技術基石使 ejabberd 很是快。它還與 XMPP 核心和相關標準高度兼容。ejabberd 能夠安裝在大多數環境中。
l Openfire:Openfire 用 Java™ 語言編寫,用戶友好,安裝方便。
本教程使用 Openfire。
爲您的 Openfire 用戶和配置建立一個新的 MySQL 數據庫。經過使用 MySQL,您能夠以編程方式從您的 PHP web 應用程序添加、編輯、刪除和查詢您的 XMPP 服務器用戶,以及調節您的 XMPP 基礎設施以匹配您的 web 基礎設施。
若是您安裝了 phpMyAdmin,好比做爲您的 XAMPP 安裝的一部分,那麼您能夠按照如下步驟建立數據庫:
1. 從主界面選擇 Privileges。
2. 選擇 Add a new user。
3. 添加用戶細節(確保主機是 localhost;本教程假定您是在 localhost 上測試),選擇 Create database with same name and grant all privileges,如 圖 1 所示。不要向您的 Openfire 數據庫新用戶授予全局數據庫特權。
圖 1. 在 phpMyAdmin 中添加一個 Openfire 數據庫
添加用戶和數據庫後,就能夠安裝 Openfire 服務器了。
下載 Openfire 安裝程序並運行它,將 Openfire 安裝到您選擇的位置(參見 參考資料)。(您也能夠選擇從 Openfire 的 Subversion 源代碼知識庫檢查 Openfire 的最新版本並本地構建它,但這個主題超出了本教程的範圍。)收到提示時,告知 Openfire 安裝程序在安裝完成時啓動服務器。
服務器啓動後,您應該看到服務器狀態窗口,如 圖 2 所示:
單擊 Launch Admin 打開一個基於 web 的嚮導,如 圖 3 所示,該向導將帶您逐步配置您的 Openfire 服務器:
配置嚮導容許您選擇使用標準數據庫鏈接或嵌入式數據庫鏈接。選擇標準數據庫鏈接,以便您可以使用您的 MySQL 數據庫。
從 Database Driver Presets 列表選擇 MySQL。將您的服務器和數據庫名稱插入 Database URL 字段。例如,對於在 localhost 上設置、名爲 openfire 的 MySQL 數據庫而言,應輸入:
jdbc:mysql://localhost:3306/openfire |
在嚮導的下一屏幕上,選擇將用戶賬戶存儲在數據庫中。輸入此前建立的數據庫用戶的用戶名和密碼,而後一直繼續到配置嚮導結束。此時,您應該已經爲您的 XMPP 服務器建立了一個服務器管理員並設置了域位置。
每用戶通知的插件
根據本教程的演示目的,您將只需使用在這裏建立的兩個用戶。可是,若是您但願在您的應用程序中支持複雜的每用戶通知,那麼您須要可以以編程方式從您的應用程序的 PHP 部分添加和移除用戶。Openfire 的 User Service 插件經過一個用於 XMPP 用戶管理的 REST 界面向您提供這個功能。要安裝這個插件,從 Openfire 插件站點(參見 參考資料)下載它。插件自己是單個文件:userservice.jar。您必須將其放置到您的 Openfire 安裝的 /plugins 目錄中。
使用您創建的管理員憑證登陸到管理屏幕。單擊 Edit Properties(位於 Server ports 下方),記錄列示的服務器名稱。這個名稱將造成您的 JIDs 的域部分。這個名稱是不可互換的,好比,不能使用 localhost
替代 127.0.0.1
,反之也不行。
單擊頂部導航菜單中的 Users/Groups 並建立兩個新用戶。這些用戶將您在開發過程當中的測試用戶。
單擊 Server settings,而後單擊 Offline messages。因爲您將 XMPP 用於界面通知,所以應將 Offline Message Policy 設置爲 Drop,如 圖 4 所示。您不想保存用戶沒有登陸時收到的消息,不然,當他們返回時,可能會被數千條通知所 「淹沒」。
在 Server settings 中,單擊 Server to Server。對於本文,您沒必要鏈接到外部服務器,由於您不須要做爲更大的 XMPP 網絡的一個鏈接部分操做。所以,將 Service Enabled 設置爲 Disabled,Allowed to Connect 設置爲 White List。這些設置將阻止未受權的鏈接形成破壞。
Openfire 在 http://localhost:7070/http-bind 維護了一個 HTTP 綁定 URL,以便經過 BOSH 訪問。要在端口 80 上使用這個 URL,您必須配置 Apache HTTP Server 以將一個 URL 轉發到這個位置。爲此,您須要啓動代理模塊。
打開您的 http.conf Apache 配置文件並找到 mod_proxy.so
和 mod_proxy_http.so
的 LoadModule
條目,它們默認被註釋掉。移除前導的井字符(#
),取消註釋。這個配置文件的 Dynamic Shared Object (DSO) Support 部分中的多個適當的行(不必定在一塊兒)如今應該相似於 清單 8:
清單 8. 啓用 Apache HTTP Server 中的代理支持
LoadModule proxy_http_module modules/mod_proxy_http.so LoadModule proxy_module modules/mod_proxy.so |
在配置文件的末尾,添加 清單 9 中的行(若是您沒有將 locahost 做爲您的測試服務器環境,則應將 127.0.0.1 替換爲您的服務器 IP 地址):
# XMPP proxy rule ProxyRequests Off ProxyPass /xmpp-httpbind http://127.0.0.1:7070/http-bind/ ProxyPassReverse /xmpp-httpbind http://127.0.0.1:7070/http-bind/ |
注意,在 清單 9 中,您在端口 80 上使用了一個稍微不一樣的 URL:/xmpp-httpbind
。這個 URL 是 strophe.js(您稍後將用到的客戶端 JavaScript 框架)分配給一個用於設置 BOSH 端點的變量的值。
重啓服務器。如今,您能夠開始編寫使用 XMPP 的 web 應用程序了。
在前面的小節中,您設置了服務器和插件。在本小節中,您將建立您的實時應用程序的服務器端部分。
您的應用程序的 PHP 端將執行如下兩個主要任務:
1. 一個 PHP 腳本將獲取一個 RSS,並將在第一個條目自您上次檢查以來發生改變時通知您。
2. 一個前端腳本將初始化您的 JavaScript XMPP 客戶端。
首先在您的測試服務器的 web 根中建立一個名爲 pingstream 的 PHP 新項目空間。若是您使用針對 Eclipse 開發環境的 PHP Development Tools 項目擴展,那麼要注意,要在 web 根、而不是 Eclipse 的默認工做空間中建立這個項目(參見 圖 5):
圖 5 顯示了這個 Pingstream 項目的如下值:
l Project name 字段:Pingstream
l Contents 單選按鈕:Create project from existing source
l Directory 字段:c:\xampp\htdocs\pingsstream
l PHP Version 單選按鈕:Use default PHP settings
XMPPHP 是應用最普遍的針對 PHP 的 XMPP 庫。與 ejabberd 和 Openfire 同樣,它也是免費和開源的,所以它是首次使用 XMPP 開發人員的一個不錯的起點。
下載 XMPPHP(參見 參考資料)。解包這個歸檔並將其插入您的新項目的 lib/xmpphp 子文件夾中。
最後,爲節約您的 RSS 提要的解析時間,下載並安裝 Last RSS PHP 解析器(參見 參考資料)。
您將使用此前建立的兩個測試用戶之一做爲您的通知發送方。在您的 /pingstream 的根中建立一個名爲 config.inc.php 的文件,並添加 清單 10 中的內容:
<?php // Pingstream configuration file // Define global $CONFIG array - don't change this! global $CONFIG; $CONFIG = array(); $CONFIG['send'] = new stdClass(); // Set account details for sending party $CONFIG['send']->user = 'testuser'; // User portion of JID $CONFIG['send']->host = '127.0.0.1'; // Host portion of JID $CONFIG['send']->resc = 'pingstream';// Resource portion of JID $CONFIG['send']->pass = 'mypass'; // Password for user |
config.inc.php 文件還必須包含關於您的接收方的細節,如 清單 11 所示:
// Set the receiving account details $CONFIG['receive'] = 'receivinguser@127.0.0.1'; |
您還需將少許數據緩存在一個文件中,所以 config.inc.php 須要包含一個可寫入的文件路徑,如 清單 12 所示:
// Full path to your cache directory, with trailing slash $CONFIG['cachedir'] = '/tmp/'; |
若是您位於一臺 Microsoft® Windows® 機器上,那麼您能夠將這個位置設置爲一個空字符串。不然,確保您指定的目錄包含一個完整路徑和一個結束斜槓,並被設置爲全局可寫入(world-writeable)。
您的 Pingstream 應用程序將檢查 IBM developerWorks Web development 專區提要獲取更新,所以,將 清單 13 中的內容添加到 config.inc.php:
清單 13. 檢查一個 developerWorks 提要獲取更新
// Set the RSS feed you're going to check $CONFIG['rss'] = 'http://www.ibm.com/developerworks/views/web/rss/libraryview.jsp'; |
如今,建立另外一個新文件:/lib.inc.php。這將是您的庫文件,包含在您的應用程序的主控制器頁面和用戶界面頁面中。
lib.inc.php 的頂端必須引用 config.inc.php(主 XMPPHP 庫)和 lastRSS,如 清單 14 所示:
<?php // Load libraries require_once('XMPPHP/XMPP.php'); require_once('config.inc.php'); require_once('lastRSS.php'); |
接下來,您將建立一個函數來經過 XMPP 將一條消息發送到客戶端。經過 XMPPHP 完成這個任務很簡單,只需使用在您的配置文件中保存的憑證創建一個鏈接,發送消息,而後關閉鏈接。
在這個函數的第一部分中,您建立了一個新的 XMPPHP_XMPP 對象,如 清單 15 所示:
// Load configuration global $CONFIG; $conn = new XMPPHP_XMPP( $CONFIG['connect']->host, 5222, $CONFIG['connect']->user, $CONFIG['connect']->pass, $CONFIG['connect']->resc); |
注意,XMPPHP 經過 XMPP 通訊的默認端口 5222 鏈接到您的 XMPP 服務器。儘管您的客戶端通訊須要使用 BOSH,但服務器端沒有這個要求。
要鏈接到 XMPP 服務器,須要發送一個初始鏈接請求,一直等到接收到您的 XMPP 會話已經啓動的通知,而後發送一個聯機狀態節來聲明您處於在線狀態(參見 清單 16):
$conn->connect(); $conn->processUntil('session_start'); $conn->presence(); |
下一步是發送消息自己,該消息已被預先填充到一個名爲 $message 的變量中(參見 清單 17):
$conn->message($CONFIG['receive'], $message); |
而後您使用 $conn->disconnect(); 斷開鏈接。
在此過程當中,您可能會遇到錯誤。清單 18 將清單 15、16 和 17 中的代碼放到一個將插入到 lib.inc.php 中的函數中。在這個過程當中,它將發送一條消息的業務封裝到一個 try/catch 語句中,該語句將把任何異常消息寫入錯誤日誌中。
清單 18. 完整的 send_notification 函數,包含 try/catch 語句以記錄錯誤
/** * Updates everyone's user interface with a message */ function send_notification($message) { // Load configuration global $CONFIG; $conn = new XMPPHP_XMPP( $CONFIG['connect']->host, 5222, $CONFIG['connect']->user, $CONFIG['connect']->pass, $CONFIG['connect']->resc); try { $conn->connect(); $conn->processUntil('session_start'); $conn->presence(); $conn->message($CONFIG['receive'], $message); $conn->disconnect(); } catch(XMPPHP_Exception $e) { error_log($e->getMessage()); } } |
您能夠對任意數量的公共通知應用程序使用這個簡單的機制。這裏,您向訪問站點的全部用戶發送相同的通知,但經過如下機制來定製通知是件麻煩事:
每一個用戶被賦予一個唯一會話字符串,或者一個特定於某個特殊興趣或搜索的共享字符串;
這個字符串做爲資源片斷被添加到接收方的 JID;
消息而後被髮送到 user@domain/session-string JID。
接下來,您須要一個函數(參見 清單 19)來從您的指定提要檢索最新的 RSS 條目:
function get_last_feed_item() { global $CONFIG; // Load configuration $rss = new lastRSS; // Initialize lastRSS $rss->CDATA = 'content'; if ($rs = $rss->get($CONFIG['rss'])) { if (isset($rs['items'][0])) return $rs['items'][0]; else return false; } } |
這個函數初始化 lastRSS,加載您配置的 RSS 提要(這裏是 IBM developerWorks Web development 專區的最新文章提要),將最頂端的條目返回爲一個數組。擁有這個最新提要條目後,您須要知道自從上次檢查以來,該條目是否被更改。爲此,您須要使用一個小型文本文件。爲保護應用程序,最好使用數據庫或另外一種方法,但對於測試目的,能夠放心使用一個小型文件緩存。這個緩存在您每次檢查時都存儲最新提要條目的 URL;若是這個 URL 更改,那麼您就有一個新條目,應該通知用戶。另外一個函數 feed_has_changed 將據此返回 true 或 false。無論是哪一種狀況,它都會將這個最新 URL 保存到緩存文件中,爲下次檢查作好準備。清單 20 展現了 feed_has_changed 函數:
function feed_has_changed($url) { global $CONFIG; $changed = false; // Check to see if the file exists, and if it does, if // the URL has changed if (!file_exists($CONFIG['cachedir'] . 'cache.txt')) { $changed = true; } else if (file_get_contents($CONFIG['cachedir'] . 'cache.txt') != $url) { $changed = true; } // If the URL has indeed changed, update the version in the cache // and return true if ($changed) { file_put_contents($CONFIG['cachedir'] . 'cache.txt', $url); return true; } // Otherwise return false return false; } |
您將把最新條目的一個簡單的 HTML 編碼版本傳遞給客戶端。在更高級的應用程序中,能夠以 JavaScript Object Notation (JSON) 或 XML 編碼該條目,多包括一些元數據,並容許客戶端 JavaScript 根據設備和瀏覽器適當格式化它。可是,對於如今,清單 21 中的版本就夠用了。
function last_item_html($item) { return <<< END <div class="item"> <div class="item_title"> <h2><a href="http://m.cnblogs.com/143136/{$item[" target="_blank" rel="nofollow"> target="_blank">{$item['title']}</a></h2> </div> <div class="item_body"> {$item['description']} </div> </div> END; } |
最後,在您的 /pingstream 目錄中建立一個名爲 backend.php 的新 PHP 文件。使用 清單 22 中的簡單 PHP 腳本做爲文件內容。這個腳本負責檢索 RSS 提要並經過 XMPP 將最新提要條目的 JSON 編碼版本發送到您的接收方。
<?php require_once('lib.inc.php'); if ($lastitem = get_last_feed_item()) { if (feed_has_changed($lastitem['link'])) { send_notification(last_item_html($lastitem)); } } |
這就是將動態通知發送給應用程序的公共用戶所需的所有內容。理想狀況下,您應該將 backend.php 腳本做爲一個按期時間任務運行。可是,對於測試目的,您能夠經過一個 web 瀏覽器手動執行該腳本。
在本小節中,您將編寫一些 JavaScript 函數,以便經過 BOSH 上的 XMPP 接收消息,並構建一個 HTML 用戶界面來顯示接收到的通知。
如今您須要建立用戶界面來接收通知。Strophe.js 是用於經過 BOSH 發送和接收 XMPP 數據的經常使用 JavaScript 庫。對於 Pingstream 中的目的,您只需接收數據,儘管有一點是顯而易見的:雙向通訊容許您快速構建豐富的協做環境。
儘管有幾個版本,但 Strophe 的 JavaScript 版本做爲一個基於瀏覽器的 XMPP 客戶端對您而言是最有用的。下載壓縮包(參見 參考資料)並將其解壓縮到 pingstream 的 strophejs 文件夾中。
jQuery JavaScript 框架極大地簡化了事件處理和 DOM 操做。本文提供的 Strophe.js 示例普遍使用該框架,這二者簡直是 「天生一對」。下載 jQuery(參見 參考資料 中的連接)並將這個縮微版放到 pingstream 中的 jquery 文件夾中。
新建一個 index.html 文件。在該文件中包含對剛纔下載的 Strophe 和 jQuery 庫的引用,以及對稍後即將定義的 pingstream.js 庫的引用。在 body
元素中,添加一個 ID 爲 notifications 的 div
元素,如 清單 23 所示:
<!DOCTYPE html> <html> <head> <title>Latest content</title> <script type="text/javascript" src="http://m.cnblogs.com/143136/jquery/jquery-1.4.2.min.js" rel="nofollow"/> <script type="text/javascript" src="http://m.cnblogs.com/143136/strophejs/strophe.js" rel="nofollow"/> <script type="text/javascript" src="http://m.cnblogs.com/143136/pingstream.js" rel="nofollow"/> </head> <body> <h1>Latest content:</h1> <div id="notifications"></div> </body> </html> |
建立 JavaScript 文件 — pingstream.js — 您剛纔在 清單 23 中引用的。在 pingstream.js 的頂端,定義此前在 Apache 中配置的 BOSH 代理端點,如 清單 24 所示:
var BOSH_SERVICE = '/xmpp-httpbind'; var connection = null; |
當頁面徹底加載後,您想自動鏈接到 XMPP 服務器。您可使用 jQuery 的 $(document).ready
調用實現這個目標;其中,您新建一個 strophe.js Strophe.Connection
對象並用它鏈接到服務器,如 清單 25 所示:
$(document).ready(function () { connection = new Strophe.Connection(BOSH_SERVICE); connection.connect( "sendinguser@127.0.0.1", "sendingpass", onConnect); }); |
更健壯的選項
對於本教程的目的,您正在使用此前定義的發送方。對於一個更健壯的應用程序,更好的方法多是爲每一個註冊應用程序用戶建立一個新用戶,並將每一個用戶訂閱到一個 「發佈-訂閱」 界面。或者,若是您將用戶名和密碼留空並將 XMPP 服務器配置爲接受這種類型的鏈接,那麼 Strophe.js 能夠匿名登陸。在這些狀況下,將針對每一個匿名用戶動態建立一個 JID;這些 JID 必須受到管理。最後,您還能夠擴展 XMPP 聊天室。
在 清單 25 中,Strophe.Connection.connect
方法包含一個對 onConnect
函數的引用,做爲它的一個參數。onConnect
將在鏈接創建後當即啓動。您能夠利用這個機會來爲入向消息添加一個通知處理程序;您在這裏註冊了一個名爲 notifyUser
函數。隨後,您發送了一個簡單的聯機狀態節。
要確保您能夠鏈接並接收新消息,您還需向用戶發送一個友好通知。
將 清單 26 中的代碼添加到您的 JavaScript 文件中的 $(document)ready
調用上方:
function onConnect(status) { $('#notifications').html('<p class="welcome">Hello! Any new posts will appear below.</p>'); connection.addHandler(notifyUser, null, 'message', null, null, null); connection.send($pres().tree()); } |
最後,因爲您註冊了通知處理程序,所以,只要 XMPP 客戶端接收到消息節,Strophe.js 就會調用 notifyUser(msg)
函數。msg
參數是 XML 節自己的一個表示,能夠如 清單 27 所示查詢:
var elems = msg.getElementsByTagName('body'); var body = elems[0]; $('#notifications').append(Strophe.getText(body)); |
理想狀況下,您但願對消息進行限制,以便只顯示您的服務器端發送用戶發送的消息。您能夠將它封裝到構成 notifyUser
函數主體的一個 if
語句中,如 清單 28 所示:
function notifyUser(msg) { if (msg.getAttribute('from') == "testuser@127.0.0.1/pingstream") { var elems = msg.getElementsByTagName('body'); var body = elems[0]; $('#notifications').append(Strophe.getText(body)); } return true; } |
這個函數應位於在 清單 26 中定義的 onConnect
函數上方。
在一個 web 瀏覽器中打開您的 index.html 文件。您應該會看到一個簡單的標題和一條消息,該消息稱更新將在下面顯示(這可能會使您回想起您發給本身的測試通知,稱 XMPP 鏈接正在成功運行)。
如今加載 backend.php。就像變戲法同樣,來自 IBM developerWorks Web development 專區的最新更新將顯示在您的頁面上。其餘帶有 RSS 提要的示例源包括 Twitter 賬戶、通信社、以及來自服務器監控軟甲的更新提要。
這是開發一個強大平臺的簡單起點。Strophe.js 可以促進應用程序的雙向通訊,儘管更簡單的方法是使用標準的 jQuery HTTP 回撥來將用戶輸入送入系統,從而避免爲您的應用程序編寫一個 XMPP 後臺監控進程的麻煩。更使人興奮的是,當您 web 服務器用做 BOSH 代理時,徹底無需太多來自服務器端 web 應用程序的輸入,兩個或更多 web 客戶端就能經過 XMPP 相互通訊。這種技術將對從辦公室協做軟件到遊戲的不少軟件產生深遠影響。
本教程討論了實時 web 應用程序的必要性,以及 XMPP 如何克服現有技術的缺點。爲展現這種方法的效果,您使用 XMPPHP、Last RSS、Strophe.js、Openfire 和 PHP 開發了一個簡單的 RSS 更新通知應用程序。
儘管須要一個附加服務器層和一些 JavaScript 新技術,但 XMPP 比傳統 Ajax 輪詢模型更加適合實時 web 應用程序。XMPP 更快,在開發和系統基礎設置方面須要的開銷更少,而且使用一個強大的新興 web 開發標準。
描述 |
名字 |
大小 |
下載方法 |
Pingstream 源代碼 |
pingstream.zip |
238KB |
l 您能夠參閱本文在 developerWorks 全球網站上的 英文原文。
l XMPP Standards Foundation:訪問 XMPP 官方站點。
l 實現可擴展消息傳遞和到場協議(XMPP)(M. Tim Jones,developerWorks,2009 年 9 月):探索 XMPP 的細節,瞭解如何將它用於簡單消息傳遞。
l 使用 XMPP、SMS、pureXML 和 PHP 建立警報系統(Joe Lennon,developerWorks,2009 年 11 月):嘗試使用 XMPP 來將通知發送到 Google Talk。
l Jabber(Gerhard Poul,developerWorks,2002 年 5 月):查看這個早期 XMPP 簡介。
l Ajax 和 XML: 將 Ajax 用於聊天(Jack D Herrington,developerWorks,2007 年 12 月):瞭解如何使用 Ajax 輪詢實現一個實時 web 應用程序。
l JavaScript Tutorial:瞭解如何使用這個 web 腳本語言。
l Ejabberd:進一步瞭解這個用 Erlang 語言編寫的 XMPP 服務器。
l My developerWorks:個性化您的 developerWorks 體驗。
l IBM XML 認證:瞭解如何才能成爲一名 IBM 認證的 XML 和相關技術的開發人員。
l XML 技術庫:訪問 developerWorks XML 專區,得到普遍的技術文章和技巧、教程、標準和 IBM 紅皮書。
l developerWorks 技術活動 和 網絡廣播:隨時關注這些活動中的技術。
l developerWorks 播客:收聽面向軟件開發人員的有趣訪談和討論。
l Openfire:下載 Openfire,這是一個基於 XMPP (Jabber) 協議的跨平臺實時協做服務器。
l PHP:訪問這個 PHP 站點,獲取這個應用普遍的腳本語言,該語言很是適合 Web 開發,能夠嵌入到 HTML 中。本教程使用 PHP 5.2 或更高版本。
l Apache HTTP Server:下載這個 Apache web 服務器。
l MySQL:下載這個開源事務型數據庫。
l DB2 Express-C:下載這個免費版 IBM DB2 數據庫服務器,它是中小型企業應用程序開發的一個堅實基礎。
l Openfire 插件:獲取用於 Openfire 的 User Service 插件。
l XMPPHP:從這個項目的 Google Code 站點下載 PHP XMPP Library。
l Last RSS:獲取 Vojtech Semecky 針對 PHP 的 RSS 解析器(通過 GNU Public License version 2 許可)。
l jQuery:下載 jQuery JavaScript 庫,通過 MIT 或 GNU Public License 許可。
l Strophe:下載 Strophe.js,這是一系列用於編寫 XMPP 客戶端的庫。它的許可容許對其進行自由使用、修改和共享。
l phpMyAdmin:獲取這個免費軟件工具,它支持經過 web 管理 MySQL。
l IBM 產品評估試用版軟件:下載或 在線試用 IBM SOA Sandbox,並開始使用來自 DB2®、Lotus®、Rational®、Tivoli® 和 WebSphere® 的應用程序開發工具和中間件產品。
l XMPP Discussion:訂閱面向開發人員、系統管理員和用戶的 XMPP 討論。
l XML 專區討論論壇:參與任何一個 XML 相關討論。
l developerWorks 博客:閱讀這些博客並參與討論。
引用:http://www.cnblogs.com/alex-blog/articles/2665665.html
補充:若是相關的環境都配置成功時,使用xmpp.php庫進行相關的消息發送若是出現以下的錯誤,那麼就是您的服務器上面的ssl沒有配置成功,具體的一個配置方法以下所示:
意思是不支持ssl,輸出phpinfo,發現本身的php環境沒有安裝ssl擴展,有些默認安裝了,有些沒有安裝
這裏寫下windows下的安裝方法
一、拷貝PHP 目錄中的libeay32.dll, ssleay32.dll, php5ts.dll, php ext 目錄下 php_curl.dll 文件到
system32 目錄。(必定要拷貝全啊,不要丟掉哪一個,否則會報錯的)
二、修改php.ini:配置好extension_dir ,去掉extension = php_curl.dll 和 extension=php_openssl.dll 前面的分號。
三、重起apache。
再次輸出phpinfo,看到以下內容就安裝成功了
這晨須要注意一下的是,您須要確認你所更改的php.ini是不是真正的配置文件,我上回就碰到了一個問題。在php的目錄下面,確實是存在一個Php.ini的文件,我改完相磁的配置之後,徹底沒有反應,依然報如上的錯誤。後面我使用phpinfo輸出一看才明白,其輸出的結果php.ini指向的文件並非這個實際文件,而是另外的一名字,然後,我在那個文件中進行了相應的修改。結果能夠正常的運行。在此提醒一下你們。