服務器推送技術與websocket協議

websocket 作為現代瀏覽器的長連接標準,可以很好的解決瀏覽器與服務器實時通訊的問題,那麼在 websocket 出現之前是怎麼解決這個問題的呢?首先來回顧一下在此之前瀏覽器和服務器的”長連接”之路。

回顧

在 websocket 協議出來之前,主要是有三種方向去實現類似 websocket 的功能的。

Flash

flash 支持 socket 通訊功能,基於 flash 可以很簡單的實現與服務器建立通訊。

優點:開發簡單、兼容性高缺點:需要瀏覽器啟用 flash 功能,並且逐漸被瀏覽器淘汰

AJAX Polling

瀏覽器使用 ajax 去輪詢服務器,服務器有內容就返回,輪詢也分為短輪詢和長輪詢。

短輪詢

短輪詢即瀏覽器通過 ajax 按照一定時間的間隔去請求服務器,服務器會立即響應,不管有沒有可用數據。

流程圖:優點:短鏈接、服務器處理方便。缺點:實時性低、很多無效請求、性能開銷大

長輪詢

長短輪詢則是瀏覽器通過 ajax 與服務器建立連接,服務器在沒有數據返回時一直阻塞著,直到有數據之後才返回響應。

流程圖:優點:實時性高缺點:每個連接只能返回一次數據

COMET

comet 也是常用的一種服務器推送技術,主要的原理是通過 HTTP Chunked 響應,將消息源源不斷的推送給瀏覽器,通常情況下服務器返回的響應內容都是定長的,會使用 Content-Length 來指定響應報文的長度,而 Chunked 編碼的響應則是通過一種特殊的編碼,只要瀏覽器沒有遇到結束標識,就會邊解析邊執行對應的響應內容。

chunked 編碼報文示例:

<code>HTTP/1.1 200 OK Content-Type: text/html Date: Thu, 08 Aug 2019 02:50:06 GMT Transfer-Encoding: chunked 11 callback('data1') 11 callback('data2') 0 /<code>

報文格式為:

<code>\r\n \r\n \r\n \r\n ... 0\r\n /<code>

由 16 進制的數字來標識一個 chunk data 數據的長度,在讀取到 0\r\n 時結束,通過一直讀取 chunk data 來執行 js 代碼,從而向客戶端推送數據。

go 語言實現:

<code>func main() { http.HandleFunc("/push", func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/html") flusher := w.(http.Flusher) w.Write([]byte(" ")) flusher.Flush() // 延遲一秒,以便觀察瀏覽器的邊解析邊執行 time.Sleep(time.Second) w.Write([]byte(" ")) flusher.Flush() }) log.Fatal(http.ListenAndServe(":8080", nil)) } /<code>

通過 iframe 實現

iframe 實現是通過隱藏一個 ,通過 iframe 連接到服務器,服務器響應帶有