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 連接到服務器,服務器響應帶有