WebSocket解析 轉載請註明出處: "WebSocket解析" 現在,很多網站為了實現推送技術,所用的技術都是輪詢。輪詢是指在特定的時間間隔(如每一秒),由瀏覽器對伺服器發起HTTP請求,然後由伺服器返回數據給瀏覽器 。由於HTTP協議是惰性的,只有客戶端發起請求,伺服器才會返回數據。輪詢技術 ...
WebSocket協議解析
轉載請註明出處:WebSocket解析
現在,很多網站為了實現推送技術,所用的技術都是輪詢。輪詢是指在特定的時間間隔(如每一秒),由瀏覽器對伺服器發起HTTP請求,然後由伺服器返回數據給瀏覽器
。由於HTTP協議是惰性的,只有客戶端發起請求,伺服器才會返回數據。輪詢技術實現的前提條件同樣是基於這種機制。而WebSocket屬於服務端推送技術,本質是一種應用層協議,可以實現持久連接的全雙工雙向通信。在介紹WebSocket之前,先談談輪詢技術和HTTP流技術。
文章目錄
- 傳統輪詢技術:Ajax短輪詢
- Comet
- Ajax長輪詢
- HTTP流
- HTML5實現服務端推送
- SSE
- WebSocket
Ajax短輪詢(Ajax Polling)
Ajax短輪詢即客戶端周期性的向伺服器發起HTTP請求,不管伺服器是否真正獲取到數據,都會向客戶端返迴響應。每個request對應一個response,由於HTTP/1.1的持久連接(建立一次TCP連接,發送多個請求)和管線化技術(非同步發送請求),使得HTTP請求可以在建立一次TCP連接之後發起多個非同步請求。
這種傳統的模式帶來很明顯的缺點,即瀏覽器需要不斷的向伺服器發出請求,然而HTTP請求在每次發送時都會帶上很長的請求頭部欄位,其中真正有效的數據可能只是很小的一部分(如Cookie欄位),顯然伺服器會浪費帶寬等資源。
有朋友可能會想,那可以加大Ajax的傳輸時間,如改為3s為一個周期。但是時間長了,對於實時性要求比較高的項目來說,頁面更新的數據也就太慢了。
Comet(服務端推送)
而比較新的技術向伺服器輪詢獲取數據的實現是Comet,即服務端推送。簡單的說,服務端推送就是在客戶端發起HTTP請求之後,伺服器可以主動的向客戶端推送數據。實現Comet的方式有兩種:Ajax長輪詢和HTTP流。
Ajax長輪詢(Ajax Long-polling)
Ajax長輪詢本身不是一個真正的推送。長輪詢是短輪詢的一種變體。在客戶端向伺服器發起HTTP請求之後,伺服器並不是每次都立即響應:當伺服器得到最新數據時,會向客戶端傳輸數據;當數據沒有更新時,伺服器會保持這個連接,等待更新數據之後,才向客戶端傳輸數據。當然,如果服務端數據長時間沒有更新,一段時間後,請求就會超時。客戶端收到超時信息後,會重新發送一個HTTP請求給伺服器。
也就是說,只有在伺服器獲取更新後的數據,才會向客戶端傳輸數據。這種方式也存在弊端。雖然服務端可以主動的向客戶端傳輸數據,但是依然需要反覆發出請求(HTTP請求數量比短輪詢少很多)。
短輪詢和長輪詢的相同點在於客戶端都需要向伺服器發起HTTP請求,不同點在於伺服器如何響應:短輪詢是伺服器立即響應,不管數據是否有效;長輪詢是等待數據更新後響應。
HTTP流
HTTP流不同於輪詢技術,HTTP流只建立一次TCP連接,在3次握手之後進行HTTP通信,此時客戶端向伺服器發起一個HTTP請求,而伺服器保持連接打開,周期性的向客戶端傳輸數據。雙方在沒有明確提出斷開連接時,伺服器就會持續向客戶端傳輸數據。也就是說,假如伺服器數據沒有更新,伺服器不會返迴響應,而是保持連接;如果數據更新了,會立即將數據傳輸給客戶端。此時會發起下一個HTTP請求,過程周而複始。
在JS中,可以通過偵聽readystatechange事件及檢測readyState的值是否為3來實現HTTP流。隨著不斷從伺服器接收數據,readyState的值會周期性的變為3。當readyState值變為3時,responseText屬性就會保存接受到的所有數據。此時,就需要比較此前接收到的數據,決定從什麼位置開始取得最新的數據。用XHR對象實現HTTP流的方式如下:
let httpStream = (url, processor, finished) => {
let xhr = new XMLHttpRequest()
let received = 0
xhr.open(url, 'get', true)
xhr.addEvetntListener('readystatechange', () => {
let result
if (xhr.readyState === 3) {
result = xhr.responseText.slice(received)
received += result.length
processor(result)
} else if (xhr.readyState === 4) {
finished(xhr.responseText)
}
})
}
只要readyState為3,就對responseText進行分隔以獲取最新數據。這裡的received表示記錄已經處理了多少字元。然後通過processor回調函數來處理最新數據。而當readyState為4時,表示數據已經完全獲取到,則直接將xhr.responseText傳入finished回調函數處理即可。
調用方式如下.
httpStream(url, data => {
console.log(data)
}, finishedData => {
console.log(data)
})
對(長、短)輪詢和HTTP流做一個小小的總結
- 傳統輪詢技術(Ajax短輪詢)是客戶端向伺服器發起HTTP請求,無論數據是否更新,伺服器都會傳輸數據。一個request對應一個response。
- 伺服器推送技術(Ajax長輪詢)是短輪詢的變種,是客戶端向伺服器發起HTTP請求,只有等待數據更新後才會傳輸數據,否則伺服器保持連接狀態。接著發起下一次HTTP請求,一個request對應一個response。
- 伺服器推送技術(HTTP流),在客戶端只發起一次HTTP請求,伺服器保持連接狀態,在數據更新之後,伺服器會傳輸數據,否則保持連接狀態。此時一個requset對應多個response。
- 無論是短輪詢、長輪詢,還是HTTP流,相同點在於都需要客戶端先發起HTTP請求。
HTML5實現服務端推送
由於伺服器推送的重要性(實現賽事結果更新、聊天室等),HTML5實現了兩個服務端推送介面,SSE和WebSocket。
SSE
SSE(Server-Sent Eevents,伺服器發送事件)用於創建到伺服器的單向連接,伺服器通過這個連接可以發送任意數量的數據。實現SSE有以下幾點要求
- 伺服器響應的MIME類型必須是text/event-stream。
- 必須按照指定的格式輸出。
用法如下,其實理解了伺服器推送之後,SSE使用起來相對簡單
// EventSource接受的參數必須同源。
// 使用message事件監聽從伺服器收到的消息,並存儲在event.data對象里。
let source = new EventSource('index.php')
source.onmessage = e => {
console.log(e.data)
}
SSE在IE下都不支持,ios4.0以上、android4.4以上都支持SSE。
WebSocket
鋪墊了那麼久的前文,終於到WebSocket了...