WebSocket 詳解

WebSocket 是什麼?

WebSocket 是一種網絡通信協議。RFC6455 定義了它的通信標準。

WebSocket 是 HTML5 開始提供的一種在單個 TCP 連接上進行全雙工通訊的協議。

在 WebSocket 規範出來之前,開發人員想實現這些實時的 Web 應用,不得不採用一些折衷的方案,其中最常用的就是輪詢 (Polling) 和 Comet 技術,而 Comet 技術實際上是輪詢技術的改進,又可細分為兩種實現方式,一種是長輪詢機制,一種稱為流技術。下面我們簡單介紹一下這幾種技術:

輪詢:

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

長輪詢:

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

流:

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

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

WebSocket 優勢

瞭解計算機網絡協議的人,應該都知道:HTTP 協議是一種無狀態的、無連接的、單向的應用層協議。它採用了請求/響應模型。通信請求只能由客戶端發起,服務端對請求做出應答處理。

這種通信模型有一個弊端:HTTP 協議無法實現服務器主動向客戶端發起消息。

這種單向請求的特點,註定瞭如果服務器有連續的狀態變化,客戶端要獲知就非常麻煩。大多數 Web 應用程序將通過頻繁的異步JavaScript和XML(AJAX)請求實現長輪詢。輪詢的效率低,非常浪費資源(因為必須不停連接,或者 HTTP 連接始終打開)。

WebSocket 詳解

因此,工程師們一直在思考,有沒有更好的方法。WebSocket 就是這樣發明的。WebSocket 連接允許客戶端和服務器之間進行全雙工通信,以便任一方都可以通過建立的連接將數據推送到另一端。WebSocket 只需要建立一次連接,就可以一直保持連接狀態。這相比於輪詢方式的不停建立連接顯然效率要大大提高。

WebSocket 詳解

輪詢和 WebSocket 實現方式的網絡負載對比圖

WebSocket 詳解

對比圖

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

WebSocket 如何工作?

Web瀏覽器和服務器都必須實現 WebSockets 協議來建立和維護連接。由於 WebSockets 連接長期存在,與典型的HTTP連接不同,對服務器有重要的影響。

基於多線程或多進程的服務器無法適用於 WebSockets,因為它旨在打開連接,儘可能快地處理請求,然後關閉連接。任何實際的 WebSockets 服務器端實現都需要一個異步服務器。

WebSocket 客戶端

在客戶端,沒有必要為 WebSockets 使用 JavaScript 庫。實現 WebSockets 的 Web 瀏覽器將通過 WebSockets 對象公開所有必需的客戶端功能(主要指支持 Html5 的瀏覽器)。

客戶端 API

以下 API 用於創建 WebSocket 對象。

var Socket = new WebSocket(url, [protocol] );

以上代碼中的第一個參數 url, 指定連接的 URL。第二個參數 protocol 是可選的,指定了可接受的子協議。

WebSocket 屬性

以下是 WebSocket 對象的屬性。假定我們使用了以上代碼創建了 Socket 對象:

屬性描述

Socket.readyState只讀屬性 readyState 表示連接狀態,可以是以下值:

  • 0 - 表示連接尚未建立。
  • 1 - 表示連接已建立,可以進行通信。
  • 2 - 表示連接正在進行關閉。
  • 3 - 表示連接已經關閉或者連接不能打開。Socket.bufferedAmount只讀屬性

bufferedAmount 已被 send() 放入正在隊列中等待傳輸,但是還沒有發出的 UTF-8 文本字節數。

WebSocket 事件

以下是 WebSocket 對象的相關事件。假定我們使用了以上代碼創建了 Socket 對象:

事件事件處理程序描述openSocket.onopen連接建立時觸發messageSocket.onmessage客戶端接收服務端數據時觸發errorSocket.onerror通信發生錯誤時觸發closeSocket.onclose連接關閉時觸發

WebSocket 方法

以下是 WebSocket 對象的相關方法。假定我們使用了以上代碼創建了 Socket 對象:

方法描述Socket.send()使用連接發送數據Socket.close()關閉連接

示例

// 初始化一個 WebSocket 對象
var ws = new WebSocket("ws://localhost:9998/echo");
// 建立 web socket 連接成功觸發事件
ws.onopen = function () {
// 使用 send() 方法發送數據
ws.send("發送數據");
alert("數據發送中...");
};
// 接收服務端數據時觸發事件
ws.onmessage = function (evt) {
var received_msg = evt.data;
alert("數據已接收...");
};
// 斷開 web socket 連接成功觸發事件
ws.onclose = function () {
alert("連接已關閉...");
};

WebSocket 規範

WebSocket 協議本質上是一個基於 TCP 的協議。為了建立一個 WebSocket 連接,客戶端瀏覽器首先要向服務器發起一個 HTTP 請求,這個請求和通常的 HTTP 請求不同,包含了一些附加頭信息,其中附加頭信息

”Upgrade: WebSocket”表明這是一個申請協議升級的 HTTP 請求,服務器端解析這些附加的頭信息然後產生應答信息返回給客戶端,客戶端和服務器端的 WebSocket 連接就建立起來了,雙方就可以通過這個連接通道自由的傳遞信息,並且這個連接會持續存在直到客戶端或者服務器端的某一方主動的關閉連接。

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

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

WebSocket 握手協議

客戶端到服務端:

GET /demo HTTP/1.1 
Host: example.com
Connection: Upgrade
Sec-WebSocket-Key2: 12998 5 Y3 1 .P00
Upgrade: WebSocket
Sec-WebSocket-Key1: 4@1 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,後面會詳細給大家推出幾個語言實現的websocet多人聊天室實現。


分享到:


相關文章: