從Ajax到WebSocket

當需要前端頻繁的請求後端數據的時候,比如說數據的實時顯示,這種情況下產生問題的核心原因是:服務器知道數據什麼時候更新,但瀏覽器不知道。
這種情況下,即使對於強大的Ajax,也是一個難以解決的問題。

解決方案1:頻繁輪詢

當我們手中只有ajax工具的時候,我們往往會選擇這樣的解決方式:
通過設定定時器,以一個固定的頻率(比如1秒/次)將ajax請求打到服務器查詢新的數據。如果後端有數據,則返回新數據,如果沒有,就返回空。

這樣的協議在請求不需要那麼頻繁並且用戶量小的時候,勉強能用。很明顯,該協議有大量的請求被浪費了,並且服務器做出了大量無效的響應。

解決方案2:長輪詢

長輪詢其實是頻繁輪詢的一個妥協,他與頻繁輪詢一樣,也是需要前端不斷的打請求,但是如果服務器沒有新數據,那麼服務器對前端的請求不做響應。

該方法會出現下面三個問題:

  1. 如果瀏覽器在服務器響應之前,有新數據發送到服務器怎麼辦?
    瀏覽器必須創建一個新的並行請求,或者終止當前請求然後創建新的請求。
  2. TCP和HTTP連接都指定了連接超時,所以服務器和客戶端必須週期性的關閉和重建連接。
  3. HTTP/1.1規範中存在着強制的連接限制。瀏覽器最多隻允許同時創建兩個到相同主機名的連接。如果一個連接長期連接到服務器等待數據推送,那麼它將減少一般可用於從服務器抓去web頁面、圖形和其他資源的連接。

在過去幾年,長輪詢得到了廣泛的應用,通常被稱爲Comet。

解決方案3: 分塊編碼

持續連接的問題:對於非持續連接,瀏覽器可以通過連接是否關閉來界定請求或響應實體的邊界;而對於持續連接,這種方法顯然不奏效。有時,儘管我已經發送完所有數據,但瀏覽器並不知道這一點,它無法得知這個打開的連接上是否還會有新數據進來,只能傻傻地等了。
爲了不讓瀏覽器等待,響應對象一般使用一個Content-Length:n頭來告知瀏覽器這個數據有多長。瀏覽器可以通過 Content-Length 的長度信息,判斷出響應實體已結束。
Content-length引入的新問題:由於 Content-Length 字段必須真實反映實體長度,但是對於動態生成的內容來說,在內容創建完之前,長度是不可知的。這時候要想準確獲取長度,只能開一個足夠大的 buffer,等內容全部生成好再計算。但這樣做一方面需要更大的內存開銷,另一方面也會讓客戶端等更久。
我們需要一個新的機制:不依賴頭部的長度信息,也能知道實體的邊界——分塊編碼(Transfer-Encoding: chunked)。

分塊編碼非常類似於長輪詢,它利用了HTTP/1.1的特性:服務器可以在不聲明內容長度的情況下響應請求。
響應在不使用Content-Length:n頭時,可以使用Transfer-Encoding:chunked頭。這樣將告訴瀏覽器 響應對象將被“分塊發送”。

利用這一特性,在開始的時候創建一個連接,只用於接受服務器發送的時間,來自服務器的每一個塊都是一個新事件,它們將出發JavaScript XMLHttpRequest對象的onreadystatechange時間處理器的調用。

儘管不如長輪詢那麼頻繁,但是連接仍然需要更新。

解決方案4: Applet和Adobe Flash

在演變的過程中,人們漸漸意識到這些解決方法所做的其實是通過單個連接來模擬全雙工通信。簡單來說,Ajax和XMLHttpRequest無法完成該任務,一個更流行儘管更短命的解決方案是Java Applet或者Adobe Flash。

開發者將創建一個含有1個像素的普通透明Applet或者Flash影片,並內嵌在網頁中,然後這個插件將創建出連接到服務器的TCP Socket連接(而不是Http連接)。這種方式消除了HTTP協議中的制約限制。

該協議在單個連接上實現了真正的全雙工通信,並消除了例如超時和兵法連接限制這樣的問題,但也帶來了很高的代價:使用第三方插件。
很顯然,這兩種技術在今天已經被淘汰了。

WebSocket

HTTP升級特性包含了如下一點:
最終我們可以使用任意的協議。
這意味着,在HTTP升級特性 在握手完成後,就可以不再使用HTTP連接,而是使用一個持久的、全雙工的TCP Socket連接。
理論上講這是行得通的,但是瀏覽器不會讓JavaScript開發者隨意使用TCP棧,所以就需要指定某些協議,因此就產生了WebSocket協議。

WebSocket連接首先將使用非正常的HTTP請求以特定的模式訪問一個URL。URL模式ws和wss分別對應於HTTP的http和https。通過一個Connection:websocket頭告訴服務器把連接升級稱爲WebScoket協議——一個全雙工持久的通信協議。

WebScoket協議的實現方式有許多優點:

  • 因爲連接在端口80(ws)或者443(wss)上創建,與HTTP使用的端口相同,幾乎所有的防火牆都不會阻塞WebSocket連接。
  • 因爲它使用HTTP握手,該協議可以集成到網絡瀏覽器和HTTP服務器中。
  • 心跳機制。
  • WebSocket連接關閉時將發送一個特殊的關閉消息,其中可以包含原因代碼和用於解釋連接被關閉原因的文本。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章