普通的輪詢。指在特定的的時間間隔(如每1秒,影響實時性與性能),由瀏覽器對服務器發出HTTP 請求,而後由服務器返回最新的數據給客戶端的瀏覽器。javascript
當服務器收到客戶端發來的請求後,服務器端不會直接進行響應,而是先將這個請求掛起,而後判斷服務器端數據是否有更新。若是有更新,則進行響應,若是一直沒有數據,直到超時(服務端設置)才返回。客戶端響應處理完服務器返回的信息後,再次發出請求,從新創建鏈接。html
1)相同點:當服務器的數據不可達時,基於http長輪詢和短輪詢 的請求,都會停留一段時間;
2)不一樣點:http長輪詢是在服務器端的停留,而http 短輪詢是在 瀏覽器端的停留;
3)性能總結:不論是長輪詢仍是短輪詢,形成服務器資源浪費。java
服務器發送事件(如下簡稱SSE)是HTML 5規範的一個組成部分,能夠實現服務器到客戶端的單向數據通訊。經過SSE,客戶端能夠自動獲取數據更新,而不用重複發送HTTP請求。一旦鏈接創建,「事件」便會自動被推送到客戶端。服務器端SSE經過「事件流(Event Stream)」的格式產生並推送事件。事件流對應的MIME類型爲「text/event-stream」,並且其基於HTTP長鏈接。web
SSE規範:https://html.spec.whatwg.org/...canvas
SSE本質是發送的不是一次性的數據包,而是一個數據流。可使用 HTTP 301 和 307 重定向與正常的 HTTP 請求同樣。服務端接二連三的發送,客戶端不會關閉鏈接,若是鏈接斷開,瀏覽器會嘗試從新鏈接。若是鏈接被關閉,客戶端能夠被告知使用 HTTP 204 無內容響應代碼中止從新鏈接。跨域
- SSE 使用 HTTP 協議,現有的服務器軟件都支持。WebSocket 是一個獨立協議。
- SSE 屬於輕量級,使用簡單;WebSocket 協議相對複雜。
- SSE 默認支持斷線重連,WebSocket 須要本身實現。
- SSE 通常只用來傳送文本,二進制數據須要編碼後傳送,WebSocket 默認支持傳送二進制數據。
- SSE 支持自定義發送的消息類型。
SSE 的客戶端 API 部署在EventSource
對象上。
使用 SSE 時,瀏覽器首先生成一個EventSource
實例,向服務器發起鏈接。瀏覽器
var source = new EventSource(url);
上面的url
能夠與當前網址同域,也能夠跨域。跨域時,能夠指定第二個參數,打開withCredentials
屬性,表示是否一塊兒發送 Cookie。服務器
var source = new EventSource(url, { withCredentials: true });
代表鏈接的當前狀態。該屬性只讀,能夠取如下值。babel
- 0:至關於常量
EventSource.CONNECTING
,表示鏈接還未創建,或者斷線正在重連。- 1:至關於常量
EventSource.OPEN
,表示鏈接已經創建,能夠接受數據。- 2:至關於常量
EventSource.CLOSED
,表示鏈接已斷,且不會重連。
一、onopen
鏈接一旦創建,就會觸發open
事件,能夠在onopen
屬性定義回調函數。網絡
source.onopen = function (event) { // ... }; // 另外一種寫法 source.addEventListener('open', function (event) { // ... }, false);
二、onmessage
客戶端收到服務器發來的數據,就會觸發message
事件,能夠在onmessage
屬性的回調函數。
source.onmessage = function (event) { var data = event.data; // handle message };
事件對象的data
屬性就是服務器端傳回的數據(文本格式)。
三、onerror
若是發生通訊錯誤(好比鏈接中斷),就會觸發error
事件,能夠在onerror
屬性定義回調函數。
source.addEventListener('error', function (event) { // handle error event }, false);
四、closeclose
方法用於關閉 SSE 鏈接。
source.close();
默認狀況下,服務器發來的數據,老是觸發瀏覽器EventSource
實例的message
事件。開發者還能夠自定義 SSE 事件,這種狀況下,發送回來的數據不會觸發message
事件。
五、自定義事件
source.addEventListener('foo', function (event) { var data = event.data; // handle message }, false);
服務器向瀏覽器發送的 SSE 數據,必須是 UTF-8 編碼的文本,具備以下的 HTTP 頭信息。
Content-Type: text/event-stream Cache-Control: no-cache Connection: keep-alive
每一次發送的信息,由若干個message
組成,每一個message
之間用nn
分隔。每一個message
內部由若干行組成,每一行都是以下格式。
[field]: valuen
上面的field
能夠取四個值。
* data * event * id * retry
data
數據內容用data
字段表示。若是數據很長,能夠分紅多行,最後一行用\n\n
結尾,不然不會觸發,前面行都用\n
結尾。
下面是一個發送 JSON 數據的例子。
data: {\n data: "foo": "bar",\n data: "baz", 555\n data: }\n\n
id
數據標識符用id
字段表示,至關於每一條數據的編號。瀏覽器用
lastEventId
屬性讀取這個值。一旦鏈接斷線,瀏覽器會發送一個 HTTP 頭,裏面包含一個特殊的Last-Event-ID
頭信息,將這個值發送回來,用來幫助服務器端重建鏈接。所以,這個頭信息能夠被視爲一種同步機制。
event
event
字段表示自定義的事件類型,默認是message
事件。瀏覽器能夠用addEventListener()
監聽該事件。
retry
retry設置當前http鏈接失敗後,從新鏈接的間隔。EventSource規範規定,客戶端在http鏈接失敗後默認進行從新鏈接,重連間隔爲3s,經過設置retry字段可指定重連間隔;兩種狀況會致使瀏覽器從新發起鏈接:一種是時間間隔到期,二是因爲網絡錯誤等緣由,致使鏈接出錯。
此外,還能夠有冒號開頭的行,表示註釋。因爲EventSource是基於HTTP鏈接之上的,所以在一段沒有數據的時期會出現超時問題。 所以須要服務端作心跳保活。
註釋行能夠用來防止鏈接超時,服務器能夠按期發送一條消息註釋行,以保持鏈接不斷。
: This is a comment
6.對於須要在客戶端與服務器之間頻繁通訊的多用途 Web 應用程序,顯然應該選擇 WebSocket。對於但願從服務器向客戶端傳輸異步數據,而不須要響應的應用程序,SSE 更適合一些。
WebSocket 協議在2008年誕生,2011年成爲國際標準。全部瀏覽器都已經支持了。
它的最大特色就是,服務器能夠主動向客戶端推送信息,客戶端也能夠主動向服務器發送信息,是真正的雙向平等對話,屬於服務器推送技術的一種。
(1)創建在 TCP 協議之上,服務器端的實現比較容易。
(2)與 HTTP 協議有着良好的兼容性。默認端口也是80和443,而且握手階段採用 HTTP 協議,所以握手時不容易屏蔽,能經過各類 HTTP 代理服務器。
(3)數據格式比較輕量,性能開銷小,通訊高效。
(4)能夠發送文本,也能夠發送二進制數據。
(5)沒有同源限制,客戶端能夠與任意服務器通訊。
(6)協議標識符是ws
(若是加密,則爲wss
),服務器網址就是 URL。
ws://example.com:80/some/path
WebSocket 對象做爲一個構造函數,用於新建 WebSocket 實例。
var ws = new WebSocket('ws://localhost:8080');
readyState
屬性返回實例對象的當前狀態,共有四種。
- CONNECTING:值爲0,表示正在鏈接。
- OPEN:值爲1,表示鏈接成功,能夠通訊了。
- CLOSING:值爲2,表示鏈接正在關閉。
- CLOSED:值爲3,表示鏈接已經關閉,或者打開鏈接失敗。
實例對象的onopen
屬性,用於指定鏈接成功後的回調函數。
ws.onopen = function () { ws.send('Hello Server!'); }
實例對象的onclose
屬性,用於指定鏈接關閉後的回調函數。
實例對象的onmessage
屬性,用於指定收到服務器數據後的回調函數。
注意,服務器數據多是文本,也多是二進制數據(blob
對象或Arraybuffer
對象)。
ws.onmessage = function(event){ if(typeof event.data === String) { console.log("Received data string"); } if(event.data instanceof ArrayBuffer){ var buffer = event.data; console.log("Received arraybuffer"); } }
除了動態判斷收到的數據類型,也可使用binaryType
屬性,顯式指定收到的二進制數據類型。
// 收到的是 blob 數據 ws.binaryType = "blob"; ws.onmessage = function(e) { console.log(e.data.size); }; // 收到的是 ArrayBuffer 數據 ws.binaryType = "arraybuffer"; ws.onmessage = function(e) { console.log(e.data.byteLength); };
實例對象的send()
方法用於向服務器發送數據。
發送文本的例子。
ws.send('your message');
發送 Blob 對象的例子。
var file = document .querySelector('input[type="file"]') .files[0]; ws.send(file);
發送 ArrayBuffer 對象的例子。
var img = canvas_context.getImageData(0, 0, 400, 320); var binary = new Uint8Array(img.data.length); for (var i = 0; i < img.data.length; i++) { binary[i] = img.data[i]; } ws.send(binary.buffer);
實例對象的bufferedAmount
屬性,表示還有多少字節的二進制數據沒有發送出去。它能夠用來判斷髮送是否結束。
var data = new ArrayBuffer(10000000); socket.send(data); if (socket.bufferedAmount === 0) { // 發送完畢 } else { // 發送還沒結束 }
實例對象的onerror
屬性,用於指定報錯時的回調函數。
服務端通常須要引庫實現,能夠查看維基百科的列表https://en.wikipedia.org/wiki...
兼容性 | 協議 | 服務器負載 | 客戶端負載 | 數據實時性 | 實現複雜度 | |
---|---|---|---|---|---|---|
SSE | IE不兼容,使用babel-polyfill進行兼容處理(IE11) | HTTP | 與傳統輪詢類似,可是佔用帶寬較少 | 瀏覽器中原生實現,佔用資源很小 | 非實時,默認3秒延遲,延遲可自定義 | 須要服務器配合,客戶端、服務端實現更簡單 |
WebSocket | 主流瀏覽器都兼容 | CPU和內存資源不以客戶端數量衡量,而是以客戶端事件數衡量。與長、短輪詢、sse相比,性能最佳。 | WS | 同Server-Sent Event。 | 實時 | 須要Socket程序實現和額外端口,客戶端實現簡單。 |