一 websocket
WebSocket是html5新增加的一種通信協議,目前流行的瀏覽器都支持這個協議,例如 Chrome,Safrie,Firefox,Opera,IE等等,對該協議支持最早的應該是chrome,從chrome12就已經開始支持,隨着協 議草案的不斷變化,各個瀏覽器對協議的實現也在不停的更新。該協議還是草案,沒有成為標准,不過成為標准應該只是時間問題了。
1. WebSocket API
首先看一段簡單的javascript代碼,該代碼調用了WebSockets的API。
var ws = new WebSocket(“ws://echo.websocket.org”);
ws.onopen = function(){ws.send(“Test!”); };
ws.onmessage = function(evt){console.log(evt.data);ws.close();};
ws.onclose = function(evt){console.log(“WebSocketClosed!”);};
ws.onerror = function(evt){console.log(“WebSocketError!”);};
這份代碼總共只有5行,現在簡單概述一下這5行代碼的意義。
第一行代碼是在申請一個WebSocket對象,參數是需要連接的服務器端的地址,同http協議使用http://開頭一樣,WebSocket協議的URL使用ws://開頭,另外安全的WebSocket協議使用wss://開頭。
第二行到第五行為WebSocket對象注冊消息的處理函數,WebSocket對象一共支持四個消息 onopen, onmessage, onclose和onerror,當Browser和WebSocketServer連接成功后,會觸發onopen消息;如果連接失敗,發送、接收數據 失敗或者處理數據出現錯誤,browser會觸發onerror消息;當Browser接收到WebSocketServer發送過來的數據時,就會觸發 onmessage消息,參數evt中包含server傳輸過來的數據;當Browser接收到WebSocketServer端發送的關閉連接請求時, 就會觸發onclose消息。我們可以看出所有的操作都是采用消息的方式觸發的,這樣就不會阻塞UI,使得UI有更快的響應時間,得到更好的用戶體驗。
2 為什么引入WebSocket協議?
Browser已經支持http協議,為什么還要開發一種新的WebSocket協議呢?我們知道http協議是一種單向的網絡協議,在建立連接后,它只 允許Browser/UA(UserAgent)向WebServer發出請求資源后,WebServer才能返回相應的數據。而WebServer不能 主動的推送數據給Browser/UA,當初這么設計http協議也是有原因的,假設WebServer能主動的推送數據給Browser/UA,那 Browser/UA就太容易受到攻擊,一些廣告商也會主動的把一些廣告信息在不經意間強行的傳輸給客戶端,這不能不說是一個災難。那么單向的http協 議給現在的網站或Web應用程序開發帶來了哪些問題呢?
讓我們來看一個案例,現在假設我們想開發一個基於Web的應用程序去獲取當前Web服務器的實時數據,例如股票的實時行情,火車票的剩余票數等等,這就需 要Browser/UA與WebServer端之間反復的進行http通信,Browser不斷的發送Get請求,去獲取當前的實時數據。下面介紹幾種常 見的方式:
1. Polling
這種方式就是通過Browser/UA定時的向Web服務器發送http的Get請求,服務器收到請求后,就把最新的數據發回給客戶端(Browser /UA),Browser/UA得到數據后,就將其顯示出來,然后再定期的重復這一過程。雖然這樣可以滿足需求,但是也仍然存在一些問題,例如在某段時間 內Web服務器端沒有更新的數據,但是Browser/UA仍然需要定時的發送Get請求過來詢問,那么Web服務器就把以前的老數據再傳送過 來,Browser/UA把這些沒有變化的數據再顯示出來,這樣顯然既浪費了網絡帶寬,又浪費了CPU的利用率。如果說把Browser發送Get請求的 周期調大一些,就可以緩解這一問題,但是如果在Web服務器端的數據更新很快時,這樣又不能保證Web應用程序獲取數據的實時性。
2. Long Polling
上面介紹了Polling遇到的問題,現在介紹一下LongPolling,它是對Polling的一種改進。
Browser/UA發送Get請求到Web服務器,這時Web服務器可以做兩件事情,第一,如果服務器端有新的數據需要傳送,就立即把數據發回給 Browser/UA,Browser/UA收到數據后,立即再發送Get請求給Web Server;第二,如果服務器端沒有新的數據需要發送,這里與Polling方法不同的是,服務器不是立即發送回應給Browser/UA,而是把這個 請求保持住,等待有新的數據到來時,再來響應這個請求;當然了,如果服務器的數據長期沒有更新,一段時間后,這個Get請求就會超 時,Browser/UA收到超時消息后,再立即發送一個新的Get請求給服務器。然后依次循環這個過程。
這種方式雖然在某種程度上減小了網絡帶寬和CPU利用率等問題,但是仍然存在缺陷,例如假設服務器端的數據更新速率較快,服務器在傳送一個數據包給 Browser后必須等待Browser的下一個Get請求到來,才能傳遞第二個更新的數據包給Browser,那么這樣的話,Browser顯示實時數 據最快的時間為2×RTT(往返時間),另外在網絡擁塞的情況下,這個應該是不能讓用戶接受的。另外,由於http數據包的頭部數據量往往很大(通常有 400多個字節),但是真正被服務器需要的數據卻很少(有時只有10個字節左右),這樣的數據包在網絡上周期性的傳輸,難免對網絡帶寬是一種浪費。
通過上面的分析可知,要是在Browser能有一種新的網絡協議,能支持客戶端和服務器端的雙向通信,而且協議的頭部又不那么龐大就好了。WebSocket就是肩負這樣一個使命登上舞台的。
3 websocket協議
WebSocket協議是一種雙向通信協議,它建立在TCP之上,同http一樣通過TCP來傳輸數據,但是它和http最大的不同有兩 點:1.WebSocket是一種雙向通信協議,在建立連接后,WebSocket服務器和Browser/UA都能主動的向對方發送或接收數據,就像 Socket一樣,不同的是WebSocket是一種建立在Web基礎上的一種簡單模擬Socket的協議;2.WebSocket需要通過握手連接,類 似於TCP它也需要客戶端和服務器端進行握手連接,連接成功后才能相互通信。
下面是一個簡單的建立握手的時序圖:
這里簡單說明一下WebSocket握手的過程。
當Web應用程序調用new WebSocket(url)接口時,Browser就開始了與地址為url的WebServer建立握手連接的過程。
1. Browser與WebSocket服務器通過TCP三次握手建立連接,如果這個建立連接失敗,那么后面的過程就不會執行,Web應用程序將收到錯誤消息通知。
2. 在TCP建立連接成功后,Browser/UA通過http協議傳送WebSocket支持的版本號,協議的字版本號,原始地址,主機地址等等一些列字段給服務器端。
例如:
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key:dGhlIHNhbXBsZSBub25jZQ==
Origin: http://example.com
Sec-WebSocket-Protocol: chat,superchat
Sec-WebSocket-Version: 13
3. WebSocket服務器收到Browser/UA發送來的握手請求后,如果數據包數據和格式正確,客戶端和服務器端的協議版本號匹配等等,就接受本次握手連接,並給出相應的數據回復,同樣回復的數據包也是采用http協議傳輸。
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept:s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
Sec-WebSocket-Protocol: chat
4. Browser收到服務器回復的數據包后,如果數據包內容、格式都沒有問題的話,就表 示本次連接成功,觸發onopen消息,此時Web開發者就可以在此時通過send接口想服務器發送數據。否則,握手連接失敗,Web應用程序會收到 onerror消息,並且能知道連接失敗的原因。
4 websocket與TCP,HTTP的關系
WebSocket與http協議一樣都是基於TCP的,所以他們都是可靠的協議,Web開發者調用的WebSocket的send函數在browser 的實現中最終都是通過TCP的系統接口進行傳輸的。WebSocket和Http協議一樣都屬於應用層的協議,那么他們之間有沒有什么關系呢?答案是肯定 的,WebSocket在建立握手連接時,數據是通過http協議傳輸的,正如我們上一節所看到的“GET/chat HTTP/1.1”,這里面用到的只是http協議一些簡單的字段。但是在建立連接之后,真正的數據傳輸階段是不需要http協議參與的。
具體關系可以參考下圖:
5 websocket server
如果要搭建一個Web服務器,我們會有很多選擇,市場上也有很多成熟的產品供我們應用,比如開源的Apache,安裝后只需簡單的配置(或者默認配置)就可 以工作了。但是如果想搭建一個WebSocket服務器就沒有那么輕松了,因為WebSocket是一種新的通信協議,目前還是草案,沒有成為標准,市場 上也沒有成熟的WebSocket服務器或者Library實現WebSocket協議,我們就必須自己動手寫代碼去解析和組裝WebSocket的數據 包。要這樣完成一個WebSocket服務器,估計所有的人都想放棄,幸好的是市場上有幾款比較好的開源庫供我們使用,比如 PyWebSocket,WebSocket-Node, LibWebSockets等等,這些庫文件已經實現了WebSocket數據包的封裝和解析,我們可以調用這些接口,這在很大程度上減少了我們的工作 量。如
下面就簡單介紹一下這些開源的庫文件。
1. PyWebSocket
PyWebSocket采用Python語言編寫,可以很好的跨平台,擴展起來也比較簡單,目前WebKit采用它搭建WebSocket服務器來做LayoutTest。
我們可以獲取源碼通過下面的命令
svn checkouthttp://pywebsocket.googlecode.com/svn/trunk/ pywebsocket-read-only
更多的詳細信息可以從http://code.google.com/p/pywebsocket/獲取。
2. WebSocket-Node
WebSocket-Node采用JavaScript語言編寫,這個庫是建立在nodejs之上的,對於熟悉JavaScript的朋友可參考一下,另外Html5和Web應用程序受歡迎的程度越來越高,nodejs也正受到廣泛的關注。
我們可以從下面的連接中獲取源碼
https://github.com/Worlize/Websocket-Node
3. LibWebSockets
LibWebSockets采用C/C++語言編寫,可定制化的力度更大,從TCP監聽開始到封包的完成我們都可以參與編程。
我們可以從下面的命令獲取源代碼
git clone git://git.warmcat.com/libwebsockets
值得一提的是:websocket是可以和http共用監聽端口的,也就是它可以公用端口完成socket任務。
二 Socket.io
node.js提供了高效的服務端運行環境,但是由於瀏覽器端對HTML5的支持不一,為了兼容所有瀏覽器,提供卓越的實時的用戶體驗,並且為程序員提供客戶端與服務端一致的編程體驗,於是socket.io誕生。Socket.io將Websocket和輪詢 (Polling)機制以及其它的實時通信方式封裝成了通用的接口,並且在服務端實現了這些實時機制的相應代碼。也就是說,Websocket僅僅是 Socket.io實現實時通信的一個子集。那么,Socket.io都實現了Polling中的那些通信機制呢?
- Adobe® Flash® Socket
- AJAX long polling
- AJAX multipart streaming
- Forever Iframe
- JSONP Polling
Adobe® Flash® Socket 大部分PC瀏覽器都支持的socket模式,不過是通過第三方嵌入到瀏覽器,不在W3C規范內,所以可能將逐步被淘汰,況且,大部分的手機瀏覽器都不支持這種模式。
AJAX long polling 這個很好理解,所有瀏覽器都支持這種方式,就是定時的向服務器發送請求,缺點是會給服務器帶來壓力並且出現信息更新不及時的現象。
AJAX multipart streaming 這是在XMLHttpRequest對象上使用某些瀏覽器(比如說Firefox)支持的multi-part標志。Ajax請求被發送給服務器端並保 持打開狀態(掛起狀態),每次需要向客戶端發送信息,就尋找一個掛起的的http請求響應給客戶端,並且所有的響應都會通過統一連接來寫入
var xhr = $.ajaxSettings.xhr(); xhr.multipart =true; xhr.open('GET', 'ajax', true); xhr.onreadystatechange = function() { if (xhr.readyState == 4) { processEvents($.parseJSON(xhr.responseText)); } }; xhr.send(null);
Forever Iframe (永存的Iframe)技術涉及了一個置於頁面中的隱藏Iframe標簽,該標簽的src屬性指向返回服務器端事件的servlet路徑。 每次在事件到達時,servlet寫入並刷新一個新的script標簽,該標簽內部帶有JavaScript代碼,iframe的內容被附加上這一 script標簽,標簽中的內容就會得到執行。這種方式的缺點是接和數據都是由瀏覽器通過HTML標簽來處理的,因此你沒有辦法知道連接何時在哪一端已被 斷開了,並且Iframe標簽在瀏覽器中將被逐步取消使用。
JSONP Polling JSONP輪詢基本上與HTTP輪詢一樣,不同之處則是JSONP可以發出跨域請求,詳細請搜索查詢jsonp的內容。