Ajax短輪詢:腳本發送的http請求
在第一種方式中,瀏覽器在收到數據后會直接調用JS回調函數,但是這種方式該如何響應數據呢?可以通過在返回數據中嵌入JS腳本的方式,如“js_func(“data from server ”)”,服務器端將返回的數據作為回調函數的參數,瀏覽器在收到數據后就會執行這段JS腳本。
但是這種方式有一個明顯的不足之處:IE、Morzilla Firefox 下端的進度欄都會顯示加載沒有完成,而且 IE 上方的圖標會不停的轉動,表示加載正在進行。Google 的天才們使用一個稱為“htmlfile”的 ActiveX 解決了在 IE 中的加載顯示問題,并將這種方法應用到了 gmail+gtalk 產品中。
Websocket:未來的解決方案1
如果說Ajax的出現是互聯網發展的必然,那么Comet技術的出現則更多透露出一種無奈,僅僅作為一種hack技術,因為沒有更好的解決方案。Comet解決的問題應該由誰來解決才是合理的呢?瀏覽器,html標準,還是http標準?主角應該是誰呢?本質上講,這涉及到數據傳輸方式,http協議應首當其沖,是時候改變一下這個懶惰的協議的請求/響應模式了。
W3C給出了答案,在新一代html標準html5中提供了一種瀏覽器和服務器間進行全雙工通訊的網絡技術Websocket。從Websocket草案得知,Websocket是一個全新的、獨立的協議,基于TCP協議,與http協議兼容、卻不會融入http協議,僅僅作為html5的一部分。于是乎腳本又被賦予了另一種能力:發起websocket請求。這種方式我們應該很熟悉,因為Ajax就是這么做的,所不同的是,Ajax發起的是http請求而已。
與http協議不同的請求/響應模式不同,Websocket在建立連接之前有一個Handshake(Opening Handshake)過程,在關閉連接前也有一個Handshake(Closing Handshake)過程,建立連接之后,雙方即可雙向通信。
有關WebSocket的詳細介,請參見即時通訊網有關WebSocket的系列文章:《WebSocket詳解(一):初步認識WebSocket技術》、《WebSocket詳解(二):技術原理、代碼演示和應用案例》、《WebSocket詳解(三):深入WebSocket通信協議細節》。
從瀏覽器支持角度來看,WebSocket已經近在眼前,但?仍有一段較長的路要走,特別是在中國這個IE6、7、8依然盛行的國家,舊版本瀏覽器的消亡需要很長一段時間,在完全實現瀏覽器全兼容前,Comet技術可能仍然是最好的解決方案。不過,當前也已存在一些比較成熟的封裝方案來解決這種兼容性限制,比如:開源的Socket.io,詳見《Socket.IO介紹:支持WebSocket、用于WEB端的即時通訊的框架》。
SSE:未來的解決方案2
SSE(Server-Sent Event,服務端推送事件)是一種允許服務端向客戶端推送新數據的HTML5技術。與由客戶端每隔幾秒從服務端輪詢拉取新數據相比,這是一種更優的解決方案。
與WebSocket?相比,它也能從服務端向客戶端推送數據。那如何決定你是用SSE還是WebSocket呢?概括來說,WebSocket能做的,SSE也能做,反之亦然,但在完成某些任務方面,它們各有千秋。
WebSocket是一種更為復雜的服務端實現技術,但它是真正的雙向傳輸技術,既能從服務端向客戶端推送數據,也能從客戶端向服務端推送數據。
WebSocket和SSE的瀏覽器支持率差不多,大多數主流桌面瀏覽器兩者都支持。在Android 4.3以及更早的版本中,系統默認瀏覽器兩者都不支持,Firefox和Chrome則完全支持;Android 4.4中,系統默認瀏覽器兩者都支持;Safari從5.0開始支持SSE(iOS系統從4.0開始),但直到6.0才正確地支持WebSocket(6.0之前的Safari所實現的WebSocket協議存在安全問題,所以一些主流瀏覽器已經禁用了基于這個協議的實現)。
與WebSocket相比,SSE有一些顯著的優勢。個人認為它最大的優勢就是便利:不需要添加任何新組件,用任何你習慣的后端語言和框架就能繼續使用。你不用為新建虛擬機、弄一個新的IP或新的端口號而勞神,就像在現有網站中新增一個頁面那樣簡單。我喜歡把這稱為既存基礎設施優勢。
SSE的第二個優勢是服務端的簡潔。相對而言,WebSocket則很復雜,不借助輔助類庫基本搞不定(我試過,令人痛苦)。
因為SSE能在現有的HTTP/HTTPS協議上運作,所以它能直接運行于現有的代理服務器和認證技術。而對WebSocket而言,代理服務器需要做一些開發(或其他工作)才能支持,在寫這本書時,很多服務器還沒有(雖然這種狀況會改善)。SSE還有一個優勢:它是一種文本協議,腳本調試非常容易。事實上,在本書中,我們會在開發和測試時用curl,甚至直接在命令行中運行后端腳本。
不過,這就引出了WebSocket相較SSE的一個潛在優勢:WebSocket是二進制協議,而SSE是文本協議(通常使用UTF-8編碼)。當然,我們可以通過SSE連接傳輸二進制數據:在SSE中,只有兩個具有特殊意義的字符,它們是CR和LF,而對它們進行轉碼并不難。但用SSE傳輸二進制數據時數據會變大,如果需要從服務端到客戶端傳輸大量的二進制數據,最好還是用WebSocket。
WebSocket相較SSE最大的優勢在于它是雙向交流的,這意味向服務端發送數據就像從服務端接收數據一樣簡單。用SSE時,一般通過一個獨立的Ajax請求從客戶端向服務端傳送數據。相對于WebSocket,這樣使用Ajax會增加開銷,但也就多一點點而已。如此一來,問題就變成了“什么時候需要關心這個差異?”如果需要以1次/秒或者更快的頻率向服務端傳輸數據,那應該用WebSocket。0.2次/秒到1次/秒的頻率是一個灰色地帶,用WebSocket和用SSE差別不大;但如果你期望重負載,那就有必要確定基準點。頻率低于0.2次/秒左右時,兩者差別不大。
從服務端向客戶端傳輸數據的性能如何?如果是文本數據而非二進制數據(如前文所提到的),SSE和WebSocket沒什么區別。它們都用TCP/IP套接字,都是輕量級協議。延遲、帶寬、服務器負載等都沒有區別,除非……呃?除非什么?
當你在享用SSE的既存基礎設施優勢,并在客戶端和服務端腳本之間設了一個網絡服務器,區別就顯現出來了。一個SSE連接不僅使用一個套接字,還會占用一個Apache線程或進程,如果用PHP,它會為這個連接專門創建一個PHP新實例。Apache和PHP會使用大量的內存,這會限制服務器所能支持的并行連接數。所以,要做到用SSE在數據傳輸性能上和WebSocket完全一樣,需要寫一個自己的后端服務器,當然,那些在任何情況下都會用自己的服務器并使用Node.js的人,會覺得這有什么稀奇的。
說一下WebSocket在舊版本瀏覽器上的兼容。?當前,大約超過2/3的瀏覽器支持這些新技術,移動端瀏覽器的支持率會低一些。依慣例,每當需要雙向套接字時,就會用到Flash,并且WebSocket的向后兼容通常是用Flash來做,這已經相當復雜了,如果瀏覽器上沒有Flash,情況更糟。概括來說,WebSocket難兼容,SSE易兼容。
有關SSE的詳細介紹文章請參見:《SSE技術詳解:一種全新的HTML5服務器推送事件技術》。