說到WebSocket技術,其實源於服務器推送技術,在現實中很多時候需要的數據是不斷變化的,比如股市數據、聊天軟件,因此就需要一種客戶端想要在不刷新頁面的情況下實時獲取到服務器端最新數據的技術,而以往的數據獲取都是基於客戶端主動請求,服務端返回對應數據。因此就有了服務器推送技術。
服務器推送技術主要由以下幾種:Ajax短輪詢、Ajax長輪詢、SSE、HTTP流、WebSocket。
一、服務器推送技術
1、Ajax短輪詢
實現簡單,客戶端(瀏覽器)定時向服務器端發送請求,獲取最新的數據。可以通過在一個定時器中觸發ajax請求來實現。
優點:實現非常簡單,JS端進行一些更改即可,無需后端服務任何改動;
缺點:輪詢的間隔過長,會導致用戶不能及時接收到更新的數據;輪詢的間隔過短,會導致查詢請求過多,增加服務器端的負擔。
代碼如下:
//每兩秒觸發一次ajax請求,獲取最新的數據 setInterval(function(){ //do some ajax call here to retrieve latest data },2000);
2、Ajax長輪詢
在Ajax輪詢的基礎上做的一點改進,在后端數據沒有更新的時候不再返回空響應,而且后端一直保存連接,直到后端有數據變化,則相應請求並且關閉連接,前端收到數據,馬上再次向后端發起請求,並處理剛剛收到的數據。
- 客戶端發起一個請求到服務器端(http request)
- 服務器端一直保持連接打開,直到有數據數據可發送給客戶端,再返回這個請求(http response)
- 客戶端收到服務器端返回的數據后,處理數據,並立馬發起一個新的請求

優點:
- 可以及時獲取到最新的數據
- 相較於輪詢策略,減少了請求數量
缺點:
服務器端要一直保持連接,不能釋放,由於一個服務器能夠處理的連接數有限,當達到服務器處理的上限的時候,服務器將無法響應新的請求
代碼如下:
function async() { $.ajax({ url: 'http://api.3g.qq.com', success: function() { async(); //success code } }); }
Servlet3 里的異步任務以及Spring帶來的DeferedResult都是使用Ajax的長連接。
3、HTTP流
//server端示例(nodejs) let express = require("express"); let app = express(); app.use(express.static("resources")); app.get("/httpstream",function(req, res){ var x = 0; res.setHeader('Connection', 'Transfer-Encoding'); res.setHeader('Content-Type', 'text/html; charset=utf-8'); res.setHeader('Transfer-Encoding', 'chunked');//聲明數據傳輸編碼為chunked,讓瀏覽器及時處理 setInterval(function(){ res.write(x+++"|"); //每隔2s向客戶端發送一次數據 },2000); }); app.listen(3000);
服務器端接收到請求后,每隔兩秒向客戶端輸出一點文字,但是不會使用res.end()
或者res.send()
結束當前http請求。
//客戶端示例js var xhr = new XMLHttpRequest(); var received = 0; var result = ""; xhr.open("get","/httpstream",true); xhr.onreadystatechange = function () { if (xhr.readyState == 3) { //readystate 3 表示正在解析數據 result = xhr.responseText.substring(received);//截取最新的數據 received += result.length; console.log(result); } } xhr.send();
readyState
會周期性的變成
3
,
responseText
包含所有的數據源。通過
received
來記錄之前已經處理過的數據長度,然后在
responseText
中截取最新的數據。

優點:
頁面的整個生命周期內,只需要建立一個http連接。
缺點:
- 如果接入的客戶端過多,服務器端會因為http連接有限而無法為新的客戶端提供服務。
- 客戶端接收到的數據流會越來越大,最終可能會引發頁面的性能問題
4、SSE(Server-Sent-Events)
SSE(Server-Sent Events)是基於HTTP實現的一套服務器向客戶端發送數據的API。他是針對上面說到的三種方法(輪詢,長輪詢,HTTP流)的一個標准API實現。使用SSE API可以創建到服務器端的單向連接,服務器可以通過這個連接發送任意數據。它有以下特點:
- 斷開自動連接
- 服務器響應的MIME類型必須是
text/event-stream
- 需要瀏覽器API支持(參考瀏覽器兼容性)
使用方法如下:
//客戶端js var source = new EventSource(url); //建立連接時觸發 source.onopen = function () { //do something here }; //從服務器端接收到新的事件時觸發 source.onmessage = function (event) { var data = event.data; //服務器返回的數據存放在event.data中 }; //連接異常時觸發 source.onerror = function () { //do something here };
客戶端創建一個EventSource對象,綁定到對應的url,然后監聽該對象的onmessage事件就可以獲取到最新的數據。
//server端示例(nodejs) let express = require("express"); let app = express(); app.use(express.static("resources")); app.get("/httpstream",function(req, res){ var x = 0; res.writeHead(200, { "Content-Type":"text/event-stream", "Cache-Control":"no-cache", "Connection":"keep-alive" }); //每個1s往客戶端發送一條數據 setInterval(function(){ res.write("data: " + x++ + "\n\n");//發送的數據格式必須是"data: <內容>/n/n" },1000); }); app.listen(3000);
5、WebSocket
5.1、什么是WebSocket?
WebSocket是html5出來的協議,http是不支持久鏈接的,WebSocket其實是一個新協議,實現了與客戶端與服務器雙向,基於消息的文本或二進制數據通信;適合於對數據的實時性要求比較強的場景,如即時通信、直播、共享桌面;后端需要單獨實現;客戶端並不是所有瀏覽器都支持。
5.2、WebSocket
通信協議兩部分?
- 一是開放性
HTTP
握手連接協商連接參數,在交換數據之前,客戶端必須與服務器協商適當的參數以建立連接 - 二是二進制消息分幀機制(接收消息的文本和二進制數據傳輸)。
WebSocket
協議提供了很多強大的特性:基於消息的通信、自定義的二進制分幀層、子協議協商、可選的協議擴展,等等。
5.3、什么是HTTP協議升級?
即先通過HTTP實現握手,然后進行協議升級,后續使用WebSocket協議進行通訊。
WebSocket和http協議有一定的交集,前端跟服務端建立websocket連接需要先發送一個htt請求,並再請求數據里表明協議需要升級為websocket。應用層通過sec-websocket-key和sec-websocket-accept標識一個唯一的websocket通道。
如下所示:
最后,前述握手完成后,如果握手成功,該連接就可以用作雙向通信信道交換WebSocket
消息。到此,客戶端與服務器之間不會再發生HTTP
通信,一切由WebSocket
協議接管。
5.4、如何協議升級
WebSocket是通過WebSocketServerHandshaker 處理服務端的websocket協議升級。
handshaker.handshake的本質是在當前pipeline添加websocket協議的編解碼器,並移除http協議編解碼器。
5.5、Websocket消息實體-WebSocketFrame
WebSocket支持多種消息實體,可以用在不同場景,例如文本、流式二進制數據、心跳檢測等。如下圖所示,不同的消息實體實現類實現不同的消息功能。
5.6、什么是WebSocket子協議-STOMP
文本定向消息協議:STOMP協議的前身是TTMP協議(一個簡單的基於文本的協議),專為消息中間件設計。它的簡單性恰巧可以用於定義webSocket的消息體格式. STOMP協議很多MQ都已支持。
二、WebSocket項目實戰演練
本博文是一個聊天室的項目實戰,代碼來自於源碼學院,GitHub地址為:https://github.com/kosamino/netty-websocket-server
代碼結構及功能如下圖所示:
系統流程圖如下圖所示:
另附另一個其余博主的WebSocket項目實戰:Netty+WebSocket 獲取火幣交易所數據項目