[Openfire]使用WebSocket創建Openfire的客戶端

  近日工做閒暇之餘,對IM系統產生了興趣,轉而研究了IM的內容。找了半天,知道比較流行的是Openfire的系統,Openfire有許多平臺實現,因爲我是作Web的,因此固然是但願尋找Web的實現。Openfire的之前的Web實現,是基於Http-bind的一種長輪詢機制,固然也沒什麼很差,只是我如今HTML5都開始了,固然但願可以來個基於Websocket的機制了。然而Google&百度了很久,也沒有找到什麼教程,發現這個東西並非很火熱的樣子,那隻好本身開始研究了。因而有了這一篇文章。java

  對了,我知道xmpp的JS框架有幾個,但那些框架彷佛都是按照本來的長輪詢的機制來作的,並非使用Websocket來作的。程序員

  另外,作軟件2年了,當我剛涉足這個行業時,我被告知:「不要重複造輪子!」,曾經我對此深信不疑,但這兩年的工做讓我愈加認識,不要重複造輪子,僅僅是在於作項目當中,作一個商業產品的時候,考慮到開發速度和產品後期的穩定性及維護性,確實須要採用成熟的技術,但!這不表明做爲一個程序員,不該該抱着一種從0開始的研究精神,若是真的熱愛這個行業,這個領域,就應該嘗試着,根據RCF文檔協議,從基礎協議的層次開始作軟件。web

    好了,廢話很少說。恩,開始正文吧。chrome

So 得先搞一個服務端是吧?


 

  確定是去 Openfire 的官網下載最新的Openfire服務端程序啦,Openfire的是開源的,能夠免費下載。如圖:後端

  1. Openfire 的主程序,主程序是服務端程序,這個主程序不是一個框架,不是一個半成品,而是一個很完整的項目,怎麼個很完整?就是你下載下來,直接雙擊運行(Windows),或到其程序的根目錄下bin文件夾,而後執行 openfire start 命令(Linux),Mac就更簡單了,直接在「設置」當中會有一個專門的管理窗口。而後它有一個Web版的管理後臺,全部設置均可以在這個後臺完成,包括安裝插件之類的。
  2. 這個火花(Spark)是Openfire搭配的客戶端,也是多平臺支持的。
  3. 這個地方能夠下載Openfire的源代碼,你能夠把他放到你的IDE中,而後簡單配置一下,就能夠run起來,這個教程網上不少,我就很少廢話了。

  

 安裝WebSocket插件


   

  這就不說了。瀏覽器

創建Websocket鏈接


 

  安裝了WebSocket插件以後,有兩個本來用於http-bind的端口,就被WebSocket佔用了(有了WebSocket,還須要Http-bind作啥= =)。這兩個接口分別是7443和7070,前者是用於HTTPS安全鏈接,後者是非安全鏈接。安全

  創建連接:服務器

 1   var connectionState = ["正在鏈接..", "鏈接已創建", "正在關閉..", "已經關閉"];
 2     var host = "ws://127.0.0.1:7070/ws/";
 3     if (window.WebSocket != 'undefined') {
 4         //OpenFire是實現了WebSocket的子協議
 5         var connection = new WebSocket(host, "xmpp");
 6         console.log(connectionState[connection.readyState]);
 7         //註冊鏈接創建時的方法
 8         connection.onopen = wsOpen;
 9         //註冊鏈接關閉時的方法
10         connection.onclose = wsClose;
11         //註冊收到消息時的方法
12         connection.onmessage = wsMsg;
13     }

  若是要使用Https加密信道,就把Host改爲:websocket

var host="wss://127.0.0.1:7443/ws/

  恩,創建安全鏈接還須要添加安全證書到Keystore,這個在Openfire的根目錄下,有一個resource/security文件夾,裏面有keyStore文件,固然,這部分我還不是很懂,關於Https的加密信道,TSL/SSl證書的概念,還沒徹底弄明白,不過這也不是這篇文章的重點。暫時我就先用非加密的方式來作,至於加密的鏈接,除了host的區別,其餘也沒有區別。session

  當Websocket握手以後,咱們要作的第一件事,就是發起一個創建流的請求,

1 function wsOpen(event) {
2         //打印連接狀態
3         console.log(connectionState[connection.readyState]);
4         //發送創建流請求
5         var steam = "<open to='127.0.0.1' from='wuxinzhe@127.0.0.1'  xmlns='urn:ietf:params:xml:ns:xmpp-framing' xml:lang='zh' version='1.0'/>";
6         connection.send(steam);
7     }

 

  Websocket下,創建流不像其餘平臺那樣,使用<stream:stream/>標籤,而是使用<open/>標籤,其中to屬性是域名,from是你的JID。

  發出請求以後,會馬上收到服務器的響應:

  第一條響應式服務器贊成創建流,第二條是告訴你,安全驗證的幾種方式,其中最簡單的方式是PLAIN方式,這種方式僅僅是將你的帳號密碼進行BASE64加密後傳輸,能夠說是很不安全。固然,你也能夠選擇SCRAM-SHA-1的安全加密方式,只不過你的js庫要支持SHA-1加密,我由於是剛開始探路,因此一切從簡,SHA-1的加密方式請求流程跟PLAIN會有一點區別,回頭我們再說。

  另外我這邊寫了一個當收到來自服務端信息的方法:

  function wsMsg(event) {
        console.log("Server: " + event.data);
    }

 

  打印出來而已。就像上面console面板中的信息同樣。那個是chrome的調試器,其餘瀏覽器也有對應的。

 

發起登陸驗證


 

  剛纔說了,我先用最基本的PLAIN的方式登陸:

   function auth() {
        //Base64編碼
        var token = window.btoa("wuxinzhe@127.0.0.1\0wuxinzhe");
        var message = "<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='PLAIN'>" + token + "</auth>";
        console.log("Client: " + message);
        connection.send(message);
    }

 

 

  其中,window.btoa()是自帶的方法,不須要額外加載任何庫,因此我才說這個真的是最快最簡單的方法,而後咱們看方法體內的字符串,這個字符串格式是:jid+password,以\0做爲分隔符,後端java程序是一個DefaultAuthProvider提供驗證的,固然大家也能夠實現本身的驗證方式,默認的方式是以\0做爲分隔符,注意的是,我一開始密碼是123456,發現\0123456這樣到後端密碼會被分割成23456,\01整個會被轉譯,恩,解決的辦法要嘛就換一個分隔符(我是說後端的openfire那邊),要嘛就禁止以數字開頭的密碼。

  發起安全驗證以後,會受到服務端的響應, 若是成功了,如圖:  

  至於要是失敗了,會返回這個錯誤,固然這個錯誤信息不多,究竟是用戶名找不到,仍是密碼錯誤了,單憑這個錯誤信息是看不出來的,不過也沒辦法咯,要不,去修改一下服務端唄:

 

  當咱們發起安全驗證成功了之後,緊接着就要開啓一個新的流,新的流服務端會給予一些新的XML節點權限(<iq/>、<presence/>),這樣才能發送一些其餘功能的信息,好比發送消息,獲取聯繫人列表,再剛開始創建的第一個流失不能發送這些節點的。

  創建新流一樣適用<open/>標籤,但有一個地方與以前不一樣,就是此次是須要攜帶id屬性的,什麼是id屬性?咱們回顧第一個流創建時,服務端返回的<open/>信息,是否是就有一個id,沒錯,這個id據個人理解,每次創建websocket鏈接時,都會爲每一個鏈接生成一個獨一無二的id,這個id表明了這個鏈接,因此後續咱們會在不少不少地方都須要使用這個id。

  發起新的流:

1 <open xmlns='jabber:client' to='127.0.0.1' version='1.0' from='wuxinzhe@127.0.0.1' id='70tvu3ooiu' xml:lang='zh'/>

  服務端會返回兩條信息,第一條,是贊成打開新流,第二條,是告訴你,接下來要作的是bind操做,就是要綁定客戶端:

  發起綁定也要用到剛纔說的id屬性:

<iq type='set' id='6ps7q3ideb'><bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'/></iq>

  綁定的時候,還能夠加入一些標籤,來對當前的客戶端,作一些比較具備語義的說明,用來描述你的客戶端類型:

<iq id="wSBRk-4" type="set">  
<bind xmlns="urn:ietf:params:xml:ns:xmpp-bind">  
<resource>Showings</resource>  
</bind>  
</iq> 

 

  咱們看看這兩種bind之間的區別:

  這是bind的客戶端請求內容和服務端響應內容,上面的是沒有加入<resource/>標籤的,下面是加入了之後。咱們能夠看到,服務端返回信息中的<jid/>是有區別的,當不加描述節點的時候,是將前面的id屬性直接用做後綴拼接如JID的,加入了之後天然會更具語義化。

  這個過程彷佛是Openfire用於區分登陸的客戶端類型的方式。這樣若是同一個帳號,在不一樣的客戶端登陸,也會有所區別。  

  而後咱們要獲取session。

<iq xmlns="jabber:client" id="ak014gz6x7" type="set"><session xmlns="urn:ietf:params:xml:ns:xmpp-session"/></iq>

  這部分我暫時還不知道用來幹嗎的,畢竟我還沒熟悉openfire的xmpp協議的整個過程,因此有些部分不是很清楚,待我整個看過之後,到時候再來看看這個步驟是作什麼用的。

  此時咱們進入Openfire的後臺,看看用戶在線的狀況:

  誒?怎麼有鏈接,倒是離線呢?不着急,由於咱們雖然登錄了,可是咱們尚未「出席」。就像QQ你能夠設置不一樣的登陸狀態,有在線、不在電腦、忙、離線,這些狀態,因此咱們若是要在線,只須要發送出席請求就好了:

<presence id="ak014gz6x7"><status>Online</status><priority>1</priority></presence>

  大家看,處處都要用到這個ID,固然,前面咱們作了綁定動做,此刻不用id屬性,換成from="jid"應該也是有效的。

  此時咱們再看後臺狀態:

  OK了。

  So咱們還要下線呢,關閉鏈接,此時要用<open/>對應的標籤<close/>

<close xmlns="urn:ietf:params:xml:ns:xmpp-framing"/>

  這樣就好了。

    其實使用Websocket創建鏈接與XMPP協議在其餘的客戶端裏是沒有什麼太大的區別,可能就是<open/><close/>這兩個標籤的區別。咱們如今可以順利登陸了,基本上,就是有一個好的開始了。

相關文章
相關標籤/搜索