天天看點

webSocket詳解,與Socket的差別

webSocket介紹與原理

WebSocket protocol 是HTML5一種新的協定。它實作了浏覽器與伺服器全雙工通信(full-duplex)。一開始的握手需要借助HTTP請求完成。

目的:即時通訊,替代輪詢

網站上的即時通訊是很常見的,比如網頁的QQ,聊天系統等。按照以往的技術能力通常是采用輪詢、Comet技術解決。

HTTP協定是非持久化的,單向的網絡協定,在建立連接配接後隻允許浏覽器向伺服器送出請求後,伺服器才能傳回相應的資料。當需要即時通訊時,通過輪詢在特定的時間間隔(如1秒),由浏覽器向伺服器發送Request請求,然後将最新的資料傳回給浏覽器。這樣的方法最明顯的缺點就是需要不斷的發送請求,而且通常HTTP request的Header是非常長的,為了傳輸一個很小的資料 需要付出巨大的代價,是很不合算的,占用了很多的寬帶。

缺點:會導緻過多不必要的請求,浪費流量和伺服器資源,每一次請求、應答,都浪費了一定流量在相同的頭部資訊上

然而WebSocket的出現可以彌補這一缺點。在WebSocket中,隻需要伺服器和浏覽器通過HTTP協定進行一個握手的動作,然後單獨建立一條TCP的通信通道進行資料的傳送。

原理

WebSocket同HTTP一樣也是應用層的協定,但是它是一種雙向通信協定,是建立在TCP之上的。

連接配接過程 —— 握手過程

1. 浏覽器、伺服器建立TCP連接配接,三次握手。這是通信的基礎,傳輸控制層,若失敗後續都不執行。
2. TCP連接配接成功後,浏覽器通過HTTP協定向伺服器傳送WebSocket支援的版本号等資訊。(開始前的HTTP握手)
3. 伺服器收到用戶端的握手請求後,同樣采用HTTP協定回饋資料。
4. 當收到了連接配接成功的消息後,通過TCP通道進行傳輸通信。
           

WebSocket與HTTP的關系

相同點

1. 都是一樣基于TCP的,都是可靠性傳輸協定。
2. 都是應用層協定。
           

不同點

1. WebSocket是雙向通信協定,模拟Socket協定,可以雙向發送或接受資訊。HTTP是單向的。
2. WebSocket是需要握手進行建立連接配接的。
           

聯系

WebSocket在建立握手時,資料是通過HTTP傳輸的。但是建立之後,在真正傳輸時候是不需要HTTP協定的。

WebSocket與Socket的關系

Socket其實并不是一個協定,而是為了友善使用TCP或UDP而抽象出來的一層,是位于應用層和傳輸控制層之間的一組接口。

Socket是應用層與TCP/IP協定族通信的中間軟體抽象層,它是一組接口。在設計模式中,Socket其實就是一個門面模式,它把複雜的TCP/IP協定族隐藏在Socket接口後面,對使用者來說,一組簡單的接口就是全部,讓Socket去組織資料,以符合指定的協定。

當兩台主機通信時,必須通過Socket連接配接,Socket則利用TCP/IP協定建立TCP連接配接。TCP連接配接則更依靠于底層的IP協定,IP協定的連接配接則依賴于鍊路層等更低層次。

WebSocket則是一個典型的應用層協定。

差別

Socket是傳輸控制層協定,WebSocket是應用層協定。

HTML5與WebSocket的關系

WebSocket API 是 HTML5 标準的一部分, 但這并不代表 WebSocket 一定要用在 HTML 中,或者隻能在基于浏覽器的應用程式中使用。

實際上,許多語言、架構和伺服器都提供了 WebSocket 支援,例如:

* 基于 C 的 libwebsocket.org
* 基于 Node.js 的 Socket.io
* 基于 Python 的 ws4py
* 基于 C++ 的 WebSocket++
* Apache 對 WebSocket 的支援: Apache Module mod_proxy_wstunnel
* Nginx 對 WebSockets 的支援: NGINX as a WebSockets Proxy 、 NGINX Announces Support for WebSocket Protocol 、WebSocket proxying
* lighttpd 對 WebSocket 的支援:mod_websocket
           

WebSocket 機制

WebSocket 是 HTML5 一種新的協定。它實作了浏覽器與伺服器全雙工通信,能更好的節省伺服器資源和帶寬并達到實時通訊,它建立在 TCP 之上,同 HTTP 一樣通過 TCP 來傳輸資料,但是它和 HTTP 最大不同是:

WebSocket 是一種雙向通信協定,在建立連接配接後,WebSocket 伺服器和 Browser/Client Agent 都能主動的向對方發送或接收資料,就像 Socket 一樣;
WebSocket 需要類似 TCP 的用戶端和伺服器端通過握手連接配接,連接配接成功後才能互相通信。
           

非 WebSocket 模式傳統 HTTP 用戶端與伺服器的互動如下圖所示:

webSocket詳解,與Socket的差別

使用 WebSocket 模式用戶端與伺服器的互動如下圖:

webSocket詳解,與Socket的差別

上圖對比可以看出,相對于傳統 HTTP 每次請求-應答都需要用戶端與服務端建立連接配接的模式,WebSocket 是類似 Socket 的 TCP 長連接配接的通訊模式,一旦 WebSocket 連接配接建立後,後續資料都以幀序列的形式傳輸。在用戶端斷開 WebSocket 連接配接或 Server 端斷掉連接配接前,不需要用戶端和服務端重新發起連接配接請求。在海量并發及用戶端與伺服器互動負載流量大的情況下,極大的節省了網絡帶寬資源的消耗,有明顯的性能優勢,且用戶端發送和接受消息是在同一個持久連接配接上發起,實時性優勢明顯。

我們再通過用戶端和服務端互動的封包看一下 WebSocket 通訊與傳統 HTTP 的不同:

在用戶端,new WebSocket 執行個體化一個新的 WebSocket 用戶端對象,連接配接類似 ws://yourdomain:port/path 的服務端 WebSocket URL,WebSocket 用戶端對象會自動解析并識别為 WebSocket 請求,進而連接配接服務端端口,執行雙方握手過程,用戶端發送資料格式類似:

清單 1.WebSocket 用戶端連接配接封包

webSocket詳解,與Socket的差別

可以看到,用戶端發起的 WebSocket 連接配接封包類似傳統 HTTP 封包,”Upgrade:websocket”參數值表明這是 WebSocket 類型請求,“Sec-WebSocket-Key”是 WebSocket 用戶端發送的一個 base64 編碼的密文,要求服務端必須傳回一個對應加密的“Sec-WebSocket-Accept”應答,否則用戶端會抛出“Error during WebSocket handshake”錯誤,并關閉連接配接。

服務端收到封包後傳回的資料格式類似:

清單 2.WebSocket 服務端響應封包

webSocket詳解,與Socket的差別

“Sec-WebSocket-Accept”的值是服務端采用與用戶端一緻的密鑰計算出來後傳回用戶端的,“HTTP/1.1 101 Switching Protocols”表示服務端接受 WebSocket 協定的用戶端連接配接,經過這樣的請求-響應處理後,用戶端服務端的 WebSocket 連接配接握手成功, 後續就可以進行 TCP 通訊了。

在開發方面,WebSocket API 也十分簡單,我們隻需要執行個體化 WebSocket,建立連接配接,然後服務端和用戶端就可以互相發送和響應消息,在下文 WebSocket 實作及案例分析部分,可以看到詳細的 WebSocket API 及代碼實作。

WebSocket 實作

如上文所述,WebSocket 的實作分為用戶端和服務端兩部分,用戶端(通常為浏覽器)發出 WebSocket 連接配接請求,服務端響應,實作類似 TCP 握手的動作,進而在浏覽器用戶端和 WebSocket 服務端之間形成一條 HTTP 長連接配接快速通道。兩者之間後續進行直接的資料互相傳送,不再需要發起連接配接和相應。

WebSocket 服務端 API

WebSocket 服務端在各個主流應用伺服器廠商中已基本獲得符合 JEE JSR356 标準規範 API 的支援,以下列舉了部分常見的商用及開源應用伺服器對 WebSocket Server 端的支援情況:

表 1.WebSocket 服務端支援

webSocket詳解,與Socket的差別

以下我們使用 Tomcat7.0.5 版本的服務端示例代碼說明 WebSocket 服務端的實作:

JSR356 的 WebSocket 規範使用 javax.websocket.*的 API,可以将一個普通 Java 對象(POJO)使用 @ServerEndpoint 注釋作為 WebSocket 伺服器的端點,代碼示例如下:

webSocket詳解,與Socket的差別

代碼解釋:

上文的簡潔代碼即建立了一個 WebSocket 的服務端,@ServerEndpoint("/echo") 的 annotation 注釋端點表示将 WebSocket 服務端運作在 ws://[Server 端 IP 或域名]:[Server 端口]/websockets/echo 的通路端點,用戶端浏覽器已經可以對 WebSocket 用戶端 API 發起 HTTP 長連接配接了。

使用 ServerEndpoint 注釋的類必須有一個公共的無參數構造函數,@onMessage 注解的 Java 方法用于接收傳入的 WebSocket 資訊,這個資訊可以是文本格式,也可以是二進制格式。

OnOpen 在這個端點一個新的連接配接建立時被調用。參數提供了連接配接的另一端的更多細節。Session 表明兩個 WebSocket 端點對話連接配接的另一端,可以了解為類似 HTTPSession 的概念。

OnClose 在連接配接被終止時調用。參數 closeReason 可封裝更多細節,如為什麼一個 WebSocket 連接配接關閉。

更進階的定制如 @Message 注釋,MaxMessageSize 屬性可以被用來定義消息位元組最大限制,在示例程式中,如果超過 6 個位元組的資訊被接收,就報告錯誤和連接配接關閉。

注意:早期不同應用伺服器支援的 WebSocket 方式不盡相同,即使同一廠商,不同版本也有細微差别,如 Tomcat 伺服器 7.0.5 以上的版本都是标準 JSR356 規範實作,而 7.0.2x/7.0.3X 的版本使用自定義 API (WebSocketServlet 和 StreamInbound, 前者是一個容器,用來初始化 WebSocket 環境;後者是用來具體處理 WebSocket 請求和響應,詳見案例分析部分),且 Tomcat7.0.3x 與 7.0.2x 的 createWebSocketInbound 方法的定義不同,增加了一個 HttpServletRequest 參數,使得可以從 request 參數中擷取更多 WebSocket 用戶端的資訊,如下代碼所示:

webSocket詳解,與Socket的差別

是以選擇 WebSocket 的 Server 端重點需要選擇其版本,通常情況下,更新的版本對 WebSocket 的支援是标準 JSR 規範 API,但也要考慮開發易用性及老版本程式移植性等方面的問題,如下文所述的客戶案例,就是因為客戶要求統一應用伺服器版本是以使用的 Tomcat 7.0.3X 版本的 WebSocketServlet 實作,而不是 JSR356 的 @ServerEndpoint 注釋端點。

webSocket詳解,與Socket的差別

用戶端 WebSocket API 基本上已經在各個主流浏覽器廠商中實作了統一,是以使用标準 HTML5 定義的 WebSocket 用戶端的 JavaScript API 即可,當然也可以使用業界滿足 WebSocket 标準規範的開源架構,如 Socket.io。

以下以一段代碼示例說明 WebSocket 的用戶端實作:

webSocket詳解,與Socket的差別

第一行代碼是在申請一個 WebSocket 對象,參數是需要連接配接的伺服器端的位址,同 HTTP 協定開頭一樣,WebSocket 協定的 URL 使用 ws://開頭,另外安全的 WebSocket 協定使用 wss://開頭。

第二行到第五行為 WebSocket 對象注冊消息的處理函數,WebSocket 對象一共支援四個消息 onopen, onmessage, onclose 和 onerror,有了這 4 個事件,我們就可以很容易很輕松的駕馭 WebSocket。

當 Browser 和 WebSocketServer 連接配接成功後,會觸發 onopen 消息;如果連接配接失敗,發送、接收資料失敗或者處理資料出現錯誤,browser 會觸發 onerror 消息;當 Browser 接收到 WebSocketServer 發送過來的資料時,就會觸發 onmessage 消息,參數 evt 中包含 Server 傳輸過來的資料;當 Browser 接收到 WebSocketServer 端發送的關閉連接配接請求時,就會觸發 onclose 消息。我們可以看出所有的操作都是采用異步回調的方式觸發,這樣不會阻塞 UI,可以獲得更快的響應時間,更好的使用者體驗。

繼續閱讀