談下WebSocket介紹,與Socket的區別

這個話題應該是面試中出現頻率比較高的吧....無論咋樣仍是有必要深刻了解下二者之間的關聯。廢話很少說,直接入題吧:java

WebSocket介紹與原理

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

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

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而抽象出來的一層,是位於應用層和傳輸控制層之間的一組接口。websocket

Socket是應用層與TCP/IP協議族通訊的中間軟件抽象層,它是一組接口。在設計模式中,Socket其實就是一個門面模式,它把複雜的TCP/IP協議族隱藏在Socket接口後面,對用戶來講,一組簡單的接口就是所有,讓Socket去組織數據,以符合指定的協議。網絡

當兩臺主機通訊時,必須經過Socket鏈接,Socket則利用TCP/IP協議創建TCP鏈接。TCP鏈接則更依靠於底層的IP協議,IP協議的鏈接則依賴於鏈路層等更低層次。session

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 的原理及運行機制。

WebSocket 是 HTML5 一種新的協議。它實現了瀏覽器與服務器全雙工通訊,能更好的節省服務器資源和帶寬並達到實時通信,它創建在 TCP 之上,同 HTTP 同樣經過 TCP 來傳輸數據,可是它和 HTTP 最大不一樣是:

  • WebSocket 是一種雙向通訊協議,在創建鏈接後,WebSocket 服務器和 Browser/Client Agent 都能主動的向對方發送或接收數據,就像 Socket 同樣;
  • WebSocket 須要相似 TCP 的客戶端和服務器端經過握手鍊接,鏈接成功後才能相互通訊。

非 WebSocket 模式傳統 HTTP 客戶端與服務器的交互以下圖所示:

圖 1. 傳統 HTTP 請求響應客戶端服務器交互圖
圖 1. 傳統 HTTP 請求響應客戶端服務器交互圖

使用 WebSocket 模式客戶端與服務器的交互以下圖:

圖 2.WebSocket 請求響應客戶端服務器交互圖
圖 2.WebSocket 請求響應客戶端服務器交互圖

上圖對比能夠看出,相對於傳統 HTTP 每次請求-應答都須要客戶端與服務端創建鏈接的模式,WebSocket 是相似 Socket 的 TCP 長鏈接的通信模式,一旦 WebSocket 鏈接創建後,後續數據都以幀序列的形式傳輸。在客戶端斷開 WebSocket 鏈接或 Server 端斷掉鏈接前,不須要客戶端和服務端從新發起鏈接請求。在海量併發及客戶端與服務器交互負載流量大的狀況下,極大的節省了網絡帶寬資源的消耗,有明顯的性能優點,且客戶端發送和接受消息是在同一個持久鏈接上發起,實時性優點明顯。

咱們再經過客戶端和服務端交互的報文看一下 WebSocket 通信與傳統 HTTP 的不一樣:

在客戶端,new WebSocket 實例化一個新的 WebSocket 客戶端對象,鏈接相似 ws://yourdomain:port/path 的服務端 WebSocket URL,WebSocket 客戶端對象會自動解析並識別爲 WebSocket 請求,從而鏈接服務端端口,執行雙方握手過程,客戶端發送數據格式相似:

清單 1.WebSocket 客戶端鏈接報文
GET /webfin/websocket/ HTTP/1.1
Host: localhost
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: xqBt3ImNzJbYqRINxEFlkg==
Origin: http://localhost:8080
Sec-WebSocket-Version: 13

能夠看到,客戶端發起的 WebSocket 鏈接報文相似傳統 HTTP 報文,」Upgrade:websocket」參數值代表這是 WebSocket 類型請求,「Sec-WebSocket-Key」是 WebSocket 客戶端發送的一個 base64 編碼的密文,要求服務端必須返回一個對應加密的「Sec-WebSocket-Accept」應答,不然客戶端會拋出「Error during WebSocket handshake」錯誤,並關閉鏈接。

服務端收到報文後返回的數據格式相似:

清單 2.WebSocket 服務端響應報文
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: K7DJLdLooIwIG/MOpvWFB3y3FE8=

「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 及客戶端 API。

WebSocket 服務端 API

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

表 1.WebSocket 服務端支持
廠商 應用服務器 備註
IBM WebSphere WebSphere 8.0 以上版本支持,7.X 以前版本結合 MQTT 支持相似的 HTTP 長鏈接
甲骨文 WebLogic WebLogic 12c 支持,11g 及 10g 版本經過 HTTP Publish 支持相似的 HTTP 長鏈接
微軟 IIS IIS 7.0+支持
Apache Tomcat Tomcat 7.0.5+支持,7.0.2X 及 7.0.3X 經過自定義 API 支持
  Jetty Jetty 7.0+支持

如下咱們使用 Tomcat7.0.5 版本的服務端示例代碼說明 WebSocket 服務端的實現:

JSR356 的 WebSocket 規範使用 javax.websocket.*的 API,能夠將一個普通 Java 對象(POJO)使用 @ServerEndpoint 註釋做爲 WebSocket 服務器的端點,代碼示例以下:

清單 3.WebSocket 服務端 API 示例
 @ServerEndpoint("/echo")
 public class EchoEndpoint {

 @OnOpen
 public void onOpen(Session session) throws IOException {
 //如下代碼省略...
 }
 
 @OnMessage
 public String onMessage(String message) {
 //如下代碼省略...
 }

 @Message(maxMessageSize=6)
 public void receiveMessage(String s) {
 //如下代碼省略...
 } 

 @OnError
 public void onError(Throwable t) {
 //如下代碼省略...
 }
 
 @OnClose
 public void onClose(Session session, CloseReason reason) {
 //如下代碼省略...
 } 
 
 }

代碼解釋:

上文的簡潔代碼即創建了一個 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 客戶端的信息,以下代碼所示:

清單 4.Tomcat7.0.3X 版本 WebSocket API
public class EchoServlet extends WebSocketServlet {
@Override
protected StreamInbound createWebSocketInbound(String subProtocol,
HttpServletRequest request) {
 //如下代碼省略....
return new MessageInbound() {
 //如下代碼省略....
}
protected void onBinaryMessage(ByteBuffer buffer)
throws IOException {
 //如下代碼省略...
}
protected void onTextMessage(CharBuffer buffer) throws IOException {
 getWsOutbound().writeTextMessage(buffer);
 //如下代碼省略...
}
};
}
}

所以選擇 WebSocket 的 Server 端重點須要選擇其版本,一般狀況下,更新的版本對 WebSocket 的支持是標準 JSR 規範 API,但也要考慮開發易用性及老版本程序移植性等方面的問題,以下文所述的客戶案例,就是由於客戶要求統一應用服務器版本因此使用的 Tomcat 7.0.3X 版本的 WebSocketServlet 實現,而不是 JSR356 的 @ServerEndpoint 註釋端點。

WebSocket 客戶端 API

對於 WebSocket 客戶端,主流的瀏覽器(包括 PC 和移動終端)現已都支持標準的 HTML5 的 WebSocket API,這意味着客戶端的 WebSocket JavaScirpt 腳本具有良好的一致性和跨平臺特性,如下列舉了常見的瀏覽器廠商對 WebSocket 的支持狀況:

表 2.WebSocket 客戶端支持
瀏覽器 支持狀況
Chrome Chrome version 4+支持
Firefox Firefox version 5+支持
IE IE version 10+支持
Safari IOS 5+支持
Android Brower Android 4.5+支持

客戶端 WebSocket API 基本上已經在各個主流瀏覽器廠商中實現了統一,所以使用標準 HTML5 定義的 WebSocket 客戶端的 JavaScript API 便可,固然也可使用業界知足 WebSocket 標準規範的開源框架,如 Socket.io。

如下以一段代碼示例說明 WebSocket 的客戶端實現:

清單 5.WebSocket 客戶端 API 示例
var ws = new WebSocket(「ws://echo.websocket.org」); 
 ws.onopen = function(){ws.send(「Test!」); }; 
 ws.onmessage = function(evt){console.log(evt.data);ws.close();}; 
 ws.onclose = function(evt){console.log(「WebSocketClosed!」);}; 
 ws.onerror = function(evt){console.log(「WebSocketError!」);};

第一行代碼是在申請一個 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,能夠得到更快的響應時間,更好的用戶體驗。

相關文章
相關標籤/搜索