使用 HTML5 WebSocket 構建即時 Web 應用程式

來源:互聯網
上載者:User

標籤:des   blog   http   java   使用   os   strong   檔案   

原文地址:http://www.ibm.com/developerworks/cn/web/1112_huangxa_websocket/

HTML5 WebSocket 簡介和實戰演練

本文主要介紹了 HTML5 WebSocket 的原理以及它給即時 Web 開發帶來的革命性的創新,並通過一個 WebSocket 伺服器和用戶端的案例來充分展示 WebSocket 的強大和易用。

作為下一代的 Web 標準,HTML5 擁有許多令人信服的新特性,如 Canvas、本機存放區、多媒體編程介面、WebSocket 等等。這其中有“Web 的 TCP ”之稱的 WebSocket 格外吸引開發人員的注意。WebSocket 的出現使得瀏覽器提供對 Socket 的支援成為可能,從而在瀏覽器和伺服器之間提供了一個基於 TCP 串連的雙向通道。Web 開發人員可以非常方便地使用 WebSocket 構建即時 web 應用,開發人員的手中從此又多了一柄神兵利器。本文首先介紹 HTML5 WebSocket 的基本概念以及這個規範試圖解決的問題,然後介紹 WebSocket 的基本原理和編程介面。接下來會通過一個簡單案例來示範怎樣實現一個 WebSocket 應用,並且展示 WebSocket 如何在功能強大和編程簡單易用上達到的完美統一。最後介紹了目前主流瀏覽器對 WebSocket 支援的狀況、局限性以及未來的展望。

即時 Web 應用程式的窘境

Web 應用程式的資訊互動過程通常是用戶端通過瀏覽器發出一個請求,伺服器端接收和審核完請求後進行處理並返回結果給用戶端,然後用戶端瀏覽器將資訊呈現出來,這種機制對於資訊變化不是特別頻繁的應用尚能相安無事,但是對於那些即時要求比較高的應用來說,比如說線上遊戲、線上證券、裝置監控、新聞線上播報、RSS 訂閱推送等等,當用戶端瀏覽器準備呈現這些資訊的時候,這些資訊在伺服器端可能已經過時了。所以保持用戶端和伺服器端的資訊同步是即時 Web 應用程式的關鍵要素,對 Web 開發人員來說也是一個難題。在 WebSocket 規範出來之前,開發人員想實現這些即時的 Web 應用程式,不得不採用一些折衷的方案,其中最常用的就是輪詢 (Polling) 和 Comet 技術,而 Comet 技術實際上是輪詢技術的改進,又可細分為兩種實現方式,一種是長輪詢機制,一種稱為流技術。下面我們簡單介紹一下這幾種技術:

輪詢

這是最早的一種實現即時 Web 應用程式的方案。用戶端以一定的時間間隔向服務端發出請求,以頻繁請求的方式來保持用戶端和伺服器端的同步。這種同步方案的最大問題是,當用戶端以固定頻率向伺服器發起請求的時候,伺服器端的資料可能並沒有更新,這樣會帶來很多無謂的網路傳輸,所以這是一種非常低效的即時方案。

長輪詢:

長輪詢是對定時輪詢的改進和提高,目地是為了降低無效的網路傳輸。當伺服器端沒有資料更新的時候,串連會保持一段時間周期直到資料或狀態改變或者時間到期,通過這種機制來減少無效的用戶端和伺服器間的互動。當然,如果服務端的資料變更非常頻繁的話,這種機制和定時輪詢比較起來沒有本質上的效能的提高。

流:

流技術方案通常就是在用戶端的頁面使用一個隱藏的視窗向服務端發出一個長串連的請求。伺服器端接到這個請求後作出回應並不斷更新串連狀態以保證用戶端和伺服器端的串連不到期。通過這種機制可以將伺服器端的資訊源源不斷地推向用戶端。這種機制在使用者體驗上有一點問題,需要針對不同的瀏覽器設計不同的方案來改進使用者體驗,同時這種機制在並發比較大的情況下,對伺服器端的資源是一個極大的考驗。

綜合這幾種方案,您會發現這些目前我們所使用的所謂的即時技術並不是真正的即時技術,它們只是在用 Ajax 方式來類比即時的效果,在每次用戶端和伺服器端互動的時候都是一次 HTTP 的請求和應答的過程,而每一次的 HTTP 要求和應答都帶有完整的 HTTP 頭資訊,這就增加了每次傳輸的資料量,而且這些方案中用戶端和伺服器端的編程實現都比較複雜,在實際的應用中,為了類比比較真實的即時效果,開發人員往往需要構造兩個 HTTP 串連來類比用戶端和伺服器之間的雙向通訊,一個串連用來處理用戶端到伺服器端的資料轉送,一個串連用來處理伺服器端到用戶端的資料轉送,這不可避免地增加了編程實現的複雜度,也增加了伺服器端的負載,制約了應用系統的擴充性。

 WebSocket 的拯救

HTML5 WebSocket 設計出來的目的就是要取代輪詢和 Comet 技術,使用戶端瀏覽器具備像 C/S 架構下案頭系統的即時通訊能力。 瀏覽器通過 JavaScript 向伺服器發出建立 WebSocket 串連的請求,串連建立以後,用戶端和伺服器端就可以通過 TCP 串連直接交換資料。因為 WebSocket 串連本質上就是一個 TCP 串連,所以在資料轉送的穩定性和資料轉送量的大小方面,和輪詢以及 Comet 技術比較,具有很大的效能優勢。Websocket.org 網站對傳統的輪詢方式和 WebSocket 調用方式作了一個詳細的測試和比較,將一個簡單的 Web 應用程式分別用輪詢方式和 WebSocket 方式來實現,在這裡引用一下他們的測試結果圖:

圖 1. 輪詢和 WebSocket 實現方式的網路負載對比圖

通過這張圖可以清楚的看出,在流量和負載增大的情況下,WebSocket 方案相比傳統的 Ajax 輪詢方案有極大的效能優勢。這也是為什麼我們認為 WebSocket 是未來即時 Web 應用程式的首選方案的原因。

 WebSocket 規範

WebSocket 通訊協定本質上是一個基於 TCP 的協議。為了建立一個 WebSocket 串連,用戶端瀏覽器首先要向伺服器發起一個 HTTP 要求,這個請求和通常的 HTTP 要求不同,包含了一些附加頭資訊,其中附加頭資訊”Upgrade: WebSocket”表明這是一個申請協議升級的 HTTP 要求,伺服器端解析這些附加的頭資訊然後產生應答資訊返回給用戶端,用戶端和伺服器端的 WebSocket 串連就建立起來了,雙方就可以通過這個串連通道自由的傳遞資訊,並且這個串連會持續存在直到用戶端或者伺服器端的某一方主動的關閉串連。

下面我們來詳細介紹一下 WebSocket 規範,由於這個規範目前還是處於草案階段,版本的變化比較快,我們選擇 draft-hixie-thewebsocketprotocol-76版本來描述 WebSocket 通訊協定。因為這個版本目前在一些主流的瀏覽器上比如 Chrome,、FireFox、Opera 上都得到比較好的支援,您如果參照的是新一些的版本話,內容可能會略有差別。

一個典型的 WebSocket 發起請求和得到響應的例子看起來如下:

清單 1. WebSocket 握手協議
用戶端到服務端: GET /demo HTTP/1.1 Host: example.com Connection: Upgrade Sec-WebSocket-Key2: 12998 5 Y3 1 .P00 Upgrade: WebSocket Sec-WebSocket-Key1: [email protected] 46546xW%0l 1 5 Origin: http://example.com [8-byte security key] 服務端到用戶端:HTTP/1.1 101 WebSocket Protocol Handshake Upgrade: WebSocket Connection: Upgrade WebSocket-Origin: http://example.com WebSocket-Location: ws://example.com/demo [16-byte hash response]

這些請求和通常的 HTTP 要求很相似,但是其中有些內容是和 WebSocket 通訊協定密切相關的。我們需要簡單介紹一下這些請求和應答資訊,”Upgrade:WebSocket”表示這是一個特殊的 HTTP 要求,請求的目的就是要將用戶端和伺服器端的通訊協議從 HTTP 協議升級到 WebSocket 通訊協定。從用戶端到伺服器端請求的資訊裡包含有”Sec-WebSocket-Key1”、“Sec-WebSocket-Key2”和”[8-byte securitykey]”這樣的頭資訊。這是用戶端瀏覽器需要向伺服器端提供的握手資訊,伺服器端解析這些頭資訊,並在握手的過程中依據這些資訊產生一個 16 位的安全密鑰並返回給用戶端,以表明伺服器端擷取了用戶端的請求,同意建立 WebSocket 串連。一旦串連建立,用戶端和伺服器端就可以通過這個通道雙向傳輸資料了。

在實際的開發過程中,為了使用 WebSocket 介面構建 Web 應用程式,我們首先需要構建一個實現了 WebSocket 規範的伺服器,伺服器端的實現不受平台和開發語言的限制,只需要遵從 WebSocket 規範即可,目前已經出現了一些比較成熟的 WebSocket 伺服器端實現,比如:

  • Kaazing WebSocket Gateway — 一個 Java 實現的 WebSocket Server
  • mod_pywebsocket — 一個 Python 實現的 WebSocket Server
  • Netty —一個 Java 實現的網路架構其中包括了對 WebSocket 的支援
  • node.js —一個 Server 端的 JavaScript 架構提供了對 WebSocket 的支援

如果以上的 WebSocket 服務端實現還不能滿足您的業務需求的話,開發人員完全可以根據 WebSocket 規範自己實現一個伺服器。在“WebSocket 實戰”這一節,我們將使用 Microsoft .NET 平台上的 C# 語言來打造一個簡單的 WebSocket 伺服器,繼而構建一個簡單的即時聊天系統。

 WebSocket JavaScript 介面

上一節介紹了 WebSocket 規範,其中主要介紹了 WebSocket 的握手協議。握手協議通常是我們在構建 WebSocket 伺服器端的實現和提供瀏覽器的 WebSocket 支援時需要考慮的問題,而針對 Web 開發人員的 WebSocket JavaScript 用戶端介面是非常簡單的,以下是 WebSocket JavaScript 介面的定義:

清單 2. WebSocket JavaScript 定義
 [Constructor(in DOMString url, in optional DOMString protocol)]  interface WebSocket {    readonly attribute DOMString URL;         // ready state    const unsigned short CONNECTING = 0;    const unsigned short OPEN = 1;    const unsigned short CLOSED = 2;    readonly attribute unsigned short readyState;    readonly attribute unsigned long bufferedAmount;    //networking    attribute Function onopen;    attribute Function onmessage;    attribute Function onclose;    boolean send(in DOMString data);    void close();  };  WebSocket implements EventTarget;

其中 URL 屬性代表 WebSocket 伺服器的網路地址,協議通常是”ws”,send 方法就是發送資料到伺服器端,close 方法就是關閉串連。除了這些方法,還有一些很重要的事件:onopen,onmessage,onerror 以及 onclose。我們借用 Nettuts 網站上的一張圖來形象的展示一下 WebSocket 介面:

圖 2. WebSocket JavaScript 介面

下面是一段簡單的 JavaScript 代碼展示了怎樣建立 WebSocket 串連和擷取資料:

清單 3. 建立 WebSocket 串連的執行個體 JavaScript 代碼
 var  wsServer = ‘ws://localhost:8888/Demo‘;  var  websocket = new WebSocket(wsServer);  websocket.onopen = function (evt) { onOpen(evt) };  websocket.onclose = function (evt) { onClose(evt) };  websocket.onmessage = function (evt) { onMessage(evt) };  websocket.onerror = function (evt) { onError(evt) };  function onOpen(evt) {  console.log("Connected to WebSocket server.");  }  function onClose(evt) {  console.log("Disconnected");  }  function onMessage(evt) {  console.log(‘Retrieved data from server: ‘ + evt.data);  }  function onError(evt) {  console.log(‘Error occured: ‘ + evt.data);  }
 瀏覽器支援

下面是主流瀏覽器對 HTML5 WebSocket 的支援情況:

瀏覽器 支援情況
Chrome Supported in version 4+
Firefox Supported in version 4+
Internet Explorer Supported in version 10+
Opera Supported in version 10+
Safari Supported in version 5+
 WebSocket 實戰

這一節裡我們用一個案例來示範怎麼使用 WebSocket 構建一個即時的 Web 應用程式。這是一個簡單的即時多人聊天系統,包括用戶端和服務端的實現。用戶端通過瀏覽器向聊天伺服器發起請求,伺服器端解析用戶端發出的握手請求併產生應答資訊返回給用戶端,從而在用戶端和伺服器之間建立串連通道。伺服器支援廣播功能,每個聊天使用者發送的資訊會即時的發送給所有的使用者,當使用者退出聊天室時,伺服器端需要清理相應使用者的串連資訊,避免資源的泄漏。以下我們分別從伺服器端和用戶端來示範這個 Web 聊天系統的實現,在實現方式上我們採用了 C# 語言來實現 WebSocket 伺服器,而用戶端是一個運行在瀏覽器裡的 HTML 檔案。

WebSocket 伺服器端實現

這個聊天伺服器的實現和基於通訊端的網路應用程式非常類似,首先是伺服器端要啟動一個通訊端監聽來自用戶端的串連請求,關鍵的區別在於 WebSocket 伺服器需要解析用戶端的 WebSocket 握手資訊,並根據 WebSocket 規範的要求產生相應的應答資訊。一旦 WebSocket 串連通道建立以後,用戶端和伺服器端的互動就和普通的通訊端網路應用程式是一樣的了。所以在下面的關於 WebSocket 伺服器端實現的描述中,我們主要闡述 WebSocket 伺服器怎樣處理 WebSocket 握手資訊,至於 WebSocket 監聽連接埠的建立,通訊端資訊流的讀取和寫入,都是一些常用的通訊端編程的方式,我們就不多做解釋了,您可以自行參閱本文的附件原始碼檔案。

在描述 WebSocket 規範時提到,一個典型的 WebSocket Upgrade 資訊如下所示:

GET /demo HTTP/1.1 Host: example.com Connection: Upgrade Sec-WebSocket-Key2: 12998 5 Y3 1 .P00 Upgrade: WebSocket Sec-WebSocket-Key1: [email protected] 46546xW%0l 1 5 Origin: http://example.com [8-byte security key]

其中 Sec-WebSocket-Key1,Sec-WebSocket-Key2 和 [8-byte security key] 這幾個頭資訊是 WebSocket 伺服器用來產生應答資訊的來源,依據draft-hixie-thewebsocketprotocol-76 草案的定義,WebSocket 伺服器基於以下的演算法來產生正確的應答資訊:

  1. 逐個字元讀取 Sec-WebSocket-Key1 頭資訊中的值,將數值型字元串連到一起放到一個臨時字串裡,同時統計所有空格的數量;
  2. 將在第 1 步裡產生的數字字串轉換成一個整型數字,然後除以第 1 步裡統計出來的空格數量,將得到的浮點數轉換成整數型;
  3. 將第 2 步裡產生的整型值轉換為符合網路傳輸的網路位元組數組;
  4. 對 Sec-WebSocket-Key2 頭資訊同樣進行第 1 到第 3 步的操作,得到另外一個網路位元組數組;
  5. 將 [8-byte security key] 和在第 3,第 4 步裡產生的網路位元組數組合并成一個 16 位元組的數組;
  6. 對第 5 步產生的位元組數組使用 MD5 演算法產生一個雜湊值,這個雜湊值就作為安全密鑰返回給用戶端,以表明伺服器端擷取了用戶端的請求,同意建立 WebSocket 串連

至此,用戶端和伺服器的 WebSocket 握手就完成了,WebSocket 通道也建立起來了。下面首先介紹一下伺服器端實現是如何根據使用者傳遞的握手資訊來產生網路位元組數組的。.NET 平台提供了很方便的對字串,數值以及數組操作的函數,所以產生位元組數組的方法還是非常簡單明了的,代碼如下:

清單 4. 產生網路位元組數組的代碼
    private byte[]   BuildServerPartialKey(string clientKey)  {       string partialServerKey = "";      byte[] currentKey;      int spacesNum = 0;      char[] keyChars = clientKey.ToCharArray();      foreach (char currentChar in keyChars)      {          if (char.IsDigit(currentChar)) partialServerKey += currentChar;         if (char.IsWhiteSpace(currentChar)) spacesNum++;      }      try      {               currentKey = BitConverter.GetBytes((int)(Int64.Parse(partialServerKey)  / spacesNum));         if (BitConverter.IsLittleEndian) Array.Reverse(currentKey);      }      catch      {         if (currentKey!= null) Array.Clear(currentKey, 0, currentKey.Length);      }      return currentKey;   }

得到網路位元組數組以後,伺服器端產生 16 位安全密鑰的方法如下所示:

清單 5. 產生 16 位安全密鑰的代碼
 private byte[] BuildCompleteServerKey(byte[] serverKey1, byte[] serverKey2,  byte[] last8Bytes)  {      byte[] concatenatedKeys = new byte[16];     Array.Copy(serverKey1, 0, concatenatedKeys, 0, 4);     Array.Copy(serverKey2, 0, concatenatedKeys, 4, 4);     Array.Copy(last8Bytes, 0, concatenatedKeys, 8, 8);     System.Security.Cryptography.MD5 MD5Service =  System.Security.Cryptography.MD5.Create();    return MD5Service.ComputeHash(concatenatedKeys);  }

整個實現是非常簡單明了的,就是將產生的網路位元組數組和用戶端提交的頭資訊裡的 [8-byte security key] 合并成一個 16 位位元組數組並用 MD5 演算法加密,然後將產生的安全密鑰作為應答資訊返回給用戶端,雙方的 WebSocekt 串連通道就建立起來了。實現了 WebSocket 握手資訊的處理邏輯,一個具有準系統的 WebSocket 伺服器就完成了。整個 WebSocket 伺服器由兩個核心類構成,一個是 WebSocketServer,另外一個是 SocketConnection,出於篇幅的考慮,我們不介紹每個類的屬性和方法了,文章的附件會給出詳細的原始碼,有興趣的讀者可以參考。

伺服器剛啟動時的畫面如下:

圖 3. WebSocket 伺服器剛啟動的畫面

用戶端可以依據這個資訊填寫聊天伺服器的串連地址,當有用戶端串連到聊天伺服器上時,伺服器會列印出用戶端和伺服器的握手資訊,每個客戶的聊天資訊也會顯示在伺服器的介面上,運行中的聊天伺服器的介面如下:

圖 4. 有用戶端串連到 WebSocket 伺服器的

以上我們簡單描述了實現一個 WebSocket 伺服器的最基本的要素,下一節我們會描述用戶端的實現。

用戶端實現

用戶端的實現相對於伺服器端的實現來說要簡單得多了,我們只需要發揮想象去設計 HTML 使用者介面,然後呼叫 WebSocket JavaScript 介面來和 WebSocket 伺服器端來互動就可以了。當然別忘了使用一個支援 HTML5 和 WebSocket 的瀏覽器,在筆者寫這篇文章的時候使用的瀏覽器是 Firefox。用戶端的頁面結構是非常簡潔的,初始運行介面如下:

圖 5. 聊天室用戶端初始頁面

當頁面初次載入的時候,首先會檢測當前的瀏覽器是否支援 WebSocket 並給出相應的提示資訊。使用者按下連線按鍵時,頁面會初始化一個到聊天伺服器的 WebSocekt 串連,初始化成功以後,頁面會載入對應的 WebSocket 事件處理函數,用戶端 JavaScript 代碼如下所示:

清單 6. 初始化用戶端 WebSocket 對象的代碼
 function ToggleConnectionClicked() {          if (SocketCreated && (ws.readyState == 0 || ws.readyState == 1)) {                  ws.close();            } else {                Log("準備串連到聊天伺服器 ...");                try {                 ws =                  new WebSocket("ws://" + document.getElementById("Connection").value);                  SocketCreated = true;                } catch (ex) {                  Log(ex, "ERROR");                  return;                }                document.getElementById("ToggleConnection").innerHTML = "斷開";                ws.onopen = WSonOpen;                ws.onmessage = WSonMessage;                ws.onclose = WSonClose;                ws.onerror = WSonError;            }        };        function WSonOpen() {            Log("串連已經建立。", "OK");            $("#SendDataContainer").show("slow");        };        function WSonMessage(event) {            Log(event.data);                    };        function WSonClose() {            Log("串連關閉。", "ERROR");            document.getElementById("ToggleConnection").innerHTML = "串連";            $("#SendDataContainer").hide("slow");        };        function WSonError() {            Log("WebSocket錯誤。", "ERROR");        };

當使用者按下發送按鈕,用戶端會調用WebSocket對象向伺服器發送資訊,並且這個訊息會廣播給所有的使用者,實現代碼如下所示:

function SendDataClicked() {            if (document.getElementById("DataToSend").value != "") {                ws.send(document.getElementById("txtName").value + "說 :\"" + document.getElementById("DataToSend").value + "\"");                document.getElementById("DataToSend").value = "";            }        };

如果有多個使用者登入到聊天伺服器,用戶端頁面的運行效果如下所示:

圖 6. 聊天用戶端運行頁面

至此我們已經完成了一個完整的 WebSocket 用戶端實現,使用者可以體驗一下這個聊天室的簡單和快捷,完全不用考慮頁面的重新整理和繁瑣的 Ajax 調用,享受傳統型程式的使用者體驗。WebSocket 的強大和易用可見一斑,您完全可以在這個基礎上加入更多的功能,設計更加漂亮的使用者介面,切身體驗 WebSocket 的震撼力。完整的用戶端代碼請參閱附件提供的原始碼。

 WebSocket 的局限性

WebSocket 的優點已經列舉得很多了,但是作為一個正在演變中的 Web 規範,我們也要看到目前用 Websocket 構建應用程式的一些風險。首先,WebSocket 規範目前還處於草案階段,也就是它的規範和 API 還是有變動的可能,另外的一個風險就是微軟的 IE 作為佔市場份額最大的瀏覽器,和其他的主流瀏覽器相比,對 HTML5 的支援是比較差的,這是我們在構建企業級的 Web 應用程式的時候必須要考慮的一個問題。

 總結

本文介紹了 HTML5 WebSocket 的橫空出世以及它嘗試解決的的問題,然後介紹了 WebSocket 規範和 WebSocket 介面,以及和傳統的即時技術相比在效能上的優勢,並且示範了怎樣使用 WebSocket 構建一個即時的 Web 應用程式,最後我們介紹了當前的主流瀏覽器對 HTML5 的支援情況和 WebSocket 的局限性。不過,我們應該看到,儘管 HTML5 WebSocket 目前還有一些局限性,但是已經是大勢所趨,微軟也明確表達了未來對 HTML5 的支援,而且這些支援我們可以在 Windows 8 和 IE10 裡看到,我們也在各種行動裝置,平板電腦上看到了 HTML5 和 WebSocket 的身影。WebSocket 將會成為未來開發即時 Web 應用程式的生力軍應該是毫無懸念的了,作為 Web 開發人員,關注 HTML5,關注 WebSocket 也應該提上議程了,否則我們在新一輪的軟體革新的浪潮中只能做壁上觀了。

 

點擊下載執行個體。

 

 

原文對websocket的講解很透徹,看了之後,瞭然了大半。厚顏將原文粘到這裡了,留日後慢慢研究。如有冒犯,請聯絡我,馬上刪除。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.