websocket瞭解---JsSIP用到---阿冬專欄!!!

WebSocket 

        (部分來自百科)

       WebSocket protocol 是HTML5一種新的協議。它實現了瀏覽器與服務器全雙工通信(full-duplex),在瀏覽器中通過http僅能實現單向的通信,comet可以一定程度上模擬雙向通信,但效率較低,並需要服務器有較好的支持; flash中的socket和xmlsocket可以實現真正的雙向通信,通過 flex ajax bridge,可以在javascript中使用這兩項功能. 可以預見,如果websocket一旦在瀏覽器中得到實現,將會替代上面兩項技術,得到廣泛的使用.面對這種狀況,HTML5定義了WebSocket協議,能更好的節省服務器資源和帶寬並達到實時通訊,在JavaEE7中也實現了WebSocket協議。

WebSocket 原理:

現很多網站爲了實現即時通訊,所用的技術都是輪詢(polling)。輪詢是在特定的的時間間隔(如每1秒),由瀏覽器對服務器發出HTTP request,然後由服務器返回最新的數據給客服端的瀏覽器。這種傳統的HTTP request 的模式帶來很明顯的缺點 – 瀏覽器需要不斷的向服務器發出請求,然而HTTP request 的header是非常長的,裏面包含的有用數據可能只是一個很小的值,這樣會佔用很多的帶寬。

而最比較新的技術去做輪詢的效果是Comet – 用了AJAX。但這種技術雖然可達到全雙工通信,但依然需要發出請求。

在 WebSocket API,瀏覽器和服務器只需要要做一個握手的動作,然後,瀏覽器和服務器之間就形成了一條快速通道。兩者之間就直接可以數據互相傳送。在此WebSocket 協議中,爲我們實現即時服務帶來了兩大好處:

1. Header

互相溝通的Header是很小的-大概只有 2 Bytes

2. Server Push

在實現websocket連線過程中,需要通過瀏覽器發出websocket連線請求,然後服務器發出迴應,這個過程通常稱爲"握手" (handshaking)

實現了websocket的瀏覽器: Chrome、Firefox、Internet Explorer、Opera、Safari。

注意:websocket api在瀏覽器端的廣泛實現似乎只是一個時間問題了, 值得注意的是服務器端沒有標準的api, 各個實現都有自己的一套api, 並且jcp也沒有類似的提案, 所以使用websocket開發服務器端有一定的風險.可能會被鎖定在某個平臺上或者將來被迫升級.


(網上資料鏈接:http://www.zhihu.com/question/20215561/answer/40316953)

      一、WebSocket是HTML5出的東西(協議),也就是說HTTP協議沒有變化,或者說沒關係,但HTTP是不支持持久連接的(長連接,循環連接的不算)
首先HTTP有1.1和1.0之說,也就是所謂的keep-alive,把多個HTTP請求合併爲一個,但是Websocket其實是一個新協議,跟HTTP協議基本沒有關係,只是爲了兼容現有瀏覽器的握手規範而已,也就是說它是HTTP協議上的一種補充可以通過這樣一張圖理解
<img src="https://pic1.zhimg.com/6651f2f811ec133b0e6d7e6d0e194b4c_b.jpg" data-rawwidth="374" data-rawheight="133" class="content_image" width="374">有交集,但是並不是全部。有交集,但是並不是全部。
另外Html5是指的一系列新的API,或者說新規範,新技術。Http協議本身只有1.0和1.1,而且跟Html本身沒有直接關係。。
通俗來說,你可以用HTTP協議傳輸非Html數據,就是這樣。再簡單來說,層級不一樣

二、Websocket是什麼樣的協議,具體有什麼優點
首先,Websocket是一個持久化的協議,相對於HTTP這種非持久的協議來說。
簡單的舉個例子吧,用目前應用比較廣泛的PHP生命週期來解釋。
1) HTTP的生命週期通過Request來界定,也就是一個Request 一個Response,那麼HTTP1.0,這次HTTP請求就結束了。
在HTTP1.1中進行了改進,使得有一個keep-alive,也就是說,在一個HTTP連接中,可以發送多個Request,接收多個Response。
但是請記住 Request = Response , 在HTTP中永遠是這樣,也就是說一個request只能有一個response。而且這個response也是被動的,不能主動發起。

     Websocket呢。。
    首先Websocket是基於HTTP協議的,或者說借用了HTTP的協議來完成一部分握手。
在握手階段是一樣的:
首先我們來看個典型的Websocket握手(借用Wikipedia的。。)
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13
Origin: http://example.com
熟悉HTTP的童鞋可能發現了,這段類似HTTP協議的握手請求中,多了幾個東西。
我會順便講解下作用。
Upgrade: websocket
   Connection: Upgrade
這個就是Websocket的核心了,告訴Apache、Nginx等服務器:“注意啦,我發起的是Websocket協議,快點幫我找到對應的助理處理~不是那個老土的HTTP。”
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13
首先,Sec-WebSocket-Key 是一個Base64 encode的值,這個是瀏覽器隨機生成的,告訴服務器:"nimei,不要忽悠wo ,我要驗證ni是不是真的是Websocket助理。"
然後,Sec_WebSocket-Protocol 是一個用戶定義的字符串,用來區分同URL下,不同的服務所需要的協議。簡單理解:今晚我要服務A,別搞錯啦~
最後,Sec-WebSocket-Version 是告訴服務器所使用的Websocket Draft(協議版本),在最初的時候,Websocket協議還在 Draft 階段,各種奇奇怪怪的協議都有,而且還有很多期奇奇怪怪不同的東西,什麼Firefox和Chrome用的不是一個版本之類的,當初Websocket協議太多可是一個大難題。。不過現在還好,已經定下來啦~大家都使用的一個東西~ 脫水:服務員,我要的是13歲的噢→_→

然後服務器會返回下列東西,表示已經接受到請求, 成功建立Websocket啦!
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=
Sec-WebSocket-Protocol: chat
這裏開始就是HTTP最後負責的區域了,告訴客戶,我已經成功切換協議啦~
Upgrade: websocket
Connection: Upgrade
依然是固定的,告訴客戶端即將升級的是Websocket協議,而不是mozillasocket,lurnarsocket或者shitsocket。
然後,Sec-WebSocket-Accept 這個則是經過服務器確認,並且加密過後的 Sec-WebSocket-Key。服務器:好啦好啦,知道啦,給你看我的ID CARD來證明行了吧。。
後面的,Sec-WebSocket-Protocol 則是表示最終使用的協議。

至此,HTTP已經完成它所有工作了,接下來就是完全按照Websocket協議進行了。
具體的協議就不在這闡述了。
你可以把 WebSocket 看成是 HTTP 協議爲了支持長連接所打的一個大補丁,它和 HTTP 有一些共性,是爲了解決 HTTP 本身無法解決的某些問題而做出的一個改良設計。在以前 HTTP 協議中所謂的 keep-alive connection 是指在一次 TCP 連接中完成多個 HTTP 請求,…顯示全部
你可以把 WebSocket 看成是 HTTP 協議爲了支持長連接所打的一個大補丁,它和 HTTP 有一些共性,是爲了解決 HTTP 本身無法解決的某些問題而做出的一個改良設計。在以前 HTTP 協議中所謂的 keep-alive connection 是指在一次 TCP 連接中完成多個 HTTP 請求,但是對每個請求仍然要單獨發 header;所謂的 polling 是指從客戶端(一般就是瀏覽器)不斷主動的向服務器發 HTTP 請求查詢是否有新數據。這兩種模式有一個共同的缺點,就是除了真正的數據部分外,服務器和客戶端還要大量交換 HTTP header,信息交換效率很低。它們建立的“長連接”都是僞.長連接,只不過好處是不需要對現有的 HTTP server 和瀏覽器架構做修改就能實現。

       WebSocket 解決的第一個問題是,通過第一個 HTTP request 建立了 TCP 連接之後,之後的交換數據都不需要再發 HTTP request了,使得這個長連接變成了一個真.長連接。但是不需要發送 HTTP header就能交換數據顯然和原有的 HTTP 協議是有區別的,所以它需要對服務器和客戶端都進行升級才能實現。在此基礎上 WebSocket 還是一個雙通道的連接,在同一個 TCP 連接上既可以發也可以收信息。此外還有 multiplexing 功能,幾個不同的 URI 可以複用同一個 WebSocket 連接。這些都是原來的 HTTP 不能做到的。

     另外說一點技術細節,因爲看到有人提問 WebSocket 可能進入某種半死不活的狀態。這實際上也是原有網絡世界的一些缺陷性設計。上面所說的 WebSocket 真.長連接雖然解決了服務器和客戶端兩邊的問題,但坑爹的是網絡應用除了服務器和客戶端之外,另一個巨大的存在是中間的網絡鏈路。一個 HTTP/WebSocket 連接往往要經過無數的路由,防火牆。你以爲你的數據是在一個“連接”中發送的,實際上它要跨越千山萬水,經過無數次轉發,過濾,才能最終抵達終點。在這過程中,中間節點的處理方法很可能會讓你意想不到。比如說,這些坑爹的中間節點可能會認爲一份連接在一段時間內沒有數據發送就等於失效,它們會自作主張的切斷這些連接。在這種情況下,不論服務器還是客戶端都不會收到任何提示,它們只會一廂情願的以爲彼此間的紅線還在,徒勞地一邊又一邊地發送抵達不了彼岸的信息。而計算機網絡協議棧的實現中又會有一層套一層的緩存,除非填滿這些緩存,你的程序根本不會發現任何錯誤。這樣,本來一個美好的 WebSocket 長連接,就可能在毫不知情的情況下進入了半死不活狀態。而解決方案,WebSocket 的設計者們也早已想過。就是讓服務器和客戶端能夠發送 Ping/Pong Frame(RFC 6455 - The WebSocket Protocol)。這種 Frame 是一種特殊的數據包,它只包含一些元數據而不需要真正的 Data Payload,可以在不影響 Application 的情況下維持住中間網絡的連接狀態。

(補充資料):
websocket協議,基於 TCP 協議;HTTP 也是一種協議,基於 TCP 協議。連接要保持還是關閉是由你服務器應用來控制的。
WebSocket 協議和 HTTP 協議是兩種不同的東西,它們扯上關係是隻是因爲:客戶端開始建立 WebSocket 連接時要發送一個 header 標記了 Upgrade 的 HTTP 請求,表示請求協議升級。所以服務器端做出響應的簡便方法是,直接在現有的 HTTP 服務器軟件和現有的端口上實現 WebSocket 協議,重用現有代碼(比如解析和認證這個 HTTP 請求。如果在 TCP 協議上實現,這兩個功能就要重新實現),然後再回一個狀態碼爲 101 的 HTTP 響應完成握手,再往後發送數據時就沒 HTTP 的事了。
        
看了補充,問題已經越來越奇怪了,變成了歷史問題。這種概念上的問題多看點書很容易解決。互聯網一共才十幾年歷史,而 WebSocket 從提議變成推薦標準就需要幾年,因爲中間要經過大量的安全驗證和實驗。Web 1 的時代人們訪問 Web 頁面是即停即走。Web 2 之後單個頁面停留時間越來越長,頁面功能越來越豐富——這時有了 RIA 的概念,改變了客戶端的編程模型——更甚至許多實時應用根本不用離開頁面,比如聊天、遊戲應用。客戶端瀏覽器決定了客戶端編程語言的能擁有的功能,以前如何做那些交互性很高的應用呢?一些技術有 XHR,iframe, 實時性要求非常高的就只能用第三方插件,比如 Flash 或 Silverlight。但 XHR 和 iframe 存在一些根本避免不了問題:1)每次交互就需要兩個 HTTP 請求 2)即使單個 HTTP 請求也要傳送很多字(header 笨重)3)客戶端不知道消息何時能夠到達,只能輪詢。服務器肯定會表示壓力很大!插件則需要額外安裝,還有安全性問題和移動設備根本不能被支持的問題。有了需要之後纔有瞭解決方案—— WebSocket 就是這種靈丹妙藥,看看主要特性:實時交互、服務器能夠主動推送內容、只需要建立一次連接、快速(延遲小,每條消息可以小到兩個字節)、開發者友好(接口簡單,並是熟悉的事件模型)等等。所以,HTML 4 選擇權很小,是否要支持 WebSocket 依據需求和環境而定;而選擇 HTML 5 的話,有了 socket 通信和圖形編程的能力,能夠開發出什麼精彩應用只取決於你的想象力。如果要找現成方案,這個 Wiki 上有很多:github.com/Modernizr/Mo

 

(補充資料三)

1. WebSocket的前世今生

2. WebSocket是什麼

3. 爲什麼使用WebSocket

4. 搭建WebSocket服務器

5. WebSocket API

6. 實例解析

以上六點分爲兩大塊,前3點側重理論,主要讓大家明白WebSocket是什麼,而後3點則結合代碼實戰,加深對WebSocket的認知。

一、WebSocket的前世今生

Web 應用的信息交互過程通常是客戶端通過瀏覽器發出一個請求,服務器端接收和審覈完請求後進行處理並返回結果給客戶端,然後客戶端瀏覽器將信息呈現出來,這種機制對於信息變化不是特別頻繁的應用尚能相安無事,但是對於那些實時要求比較高的應用來說就顯得捉襟見肘了。我們需要一種高效節能的雙向通信機制來保證數據的實時傳輸。有web TCP之稱的WebSocket應運而生,給開發人員提供了一把強有力的武器來解決疑難雜症。

(PS:其實,在早期的HTML5規範中,並沒有包含WebSocket的定義,一些早期的HTML5書籍中,完全沒有WebSocket的介紹。直到後來,才加入到當前的草案中。)

二、WebSocket是什麼?

其實,從背景介紹中,我們大致的可以猜出,WebSocket是幹什麼用的。前面我們提到,WebSocket有web TCP之稱,既然是TCP,肯定是用來做通信的,但是它又有不同的地方,WebSocket作爲HTML5中新增的一種通信協議,由通信協議和編程API組成,它能夠在瀏覽器和服務器之間建立雙向連接,以基於事件的方式,賦予瀏覽器原生的實時通信能力,來擴展我們的web應用,增加用戶體驗,提升應用的性能。何謂雙向?服務器端和客戶端可以同時發送並響應請求,而不再像HTTP的請求和響應。

三、爲什麼使用WebSocket

在WebSocket出現之前,我們有一些其它的實時通訊方案,比較常用的有輪詢,長輪詢,流,還有基於Flash的交換數據的方式,接下來,我們一一分析一下,各種通信方式的特點。

① 輪詢

這是最早的一種實現實時web應用的方案;原理比較簡單易懂,就是客戶端以一定的時間間隔向服務器發送請求,以頻繁請求的方式來保持客戶端和服務器端的數據同步。但是問題也很明顯:當客戶端以固定頻率向服務器端發送請求時,服務器端的數據可能並沒有更新,這樣會帶來很多無謂的請求,浪費帶寬,效率低下。

② 長輪詢

長輪詢是對定時輪詢的改進和提高,目地是爲了降低無效的網絡傳輸。當服務器端沒有數據更新的時候,連接會保持一段時間週期直到數據或狀態改變或者時間過期,通過這種機制來減少無效的客戶端和服務器間的交互。當然,如果服務端的數據變更非常頻繁的話,這種機制和定時輪詢比較起來沒有本質上的性能的提高。

③ 流

長輪詢是對定時輪詢的改進和提高,目地是爲了降低無效的網絡傳輸。當服務器端沒有數據更新的時候,連接會保持一段時間週期直到數據或狀態改變或者時間過期,通過這種機制來減少無效的客戶端和服務器間的交互。當然,如果服務端的數據變更非常頻繁的話,這種機制和定時輪詢比較起來沒有本質上的性能的提高。

④ 基於Flash的實時通訊方式

Flash有自己的socket實現,這爲實時通信提供了可能。我們可以利用Flash完成數據交換,再利用Flash暴露出相應的接口,方便JavaScript調用,來達到實時傳輸數據的目的。這種方式比前面三種方式都要高效,而且應用場景比較廣泛;因爲flash本身的安裝率很高;但是在當前的互聯網環境下,移動終端對flash的支持並不好,以IOS爲主的系統中根本沒有flash的存在,而在android陣營中,雖然有flash的支持,但實際的使用效果差強人意,即使是配置較高的移動設備,也很難讓人滿意。就在前幾天(2012年6月底),Adobe官方宣佈,不在支持android4.1以後的系統,這基本上宣告了flash在移動終端上的死亡。

下面是輪詢和長輪詢的信息流轉圖:


    對比完四種不同的實時通信方式,不難發現,除了基於flash的方案外,其它三種方式都是用AJAX方式來模擬實時的效果,每次客戶端和服務器端交互時,都是一次完整的HTTP請求和應答的過程,而每一次的HTTP請求和應答都帶有完整的HTTP頭信息,這就增加每次的數據傳輸量,而且這些方案中客戶端和服務端的編程實現比較複雜。

接下來,我們再來看一下WebSocket,爲什麼要使用它呢?高效節能,簡單易用。

下圖是來自websocket.org的測試結果:

在流量和負載增大的情況下,WebSocket 方案相比傳統的 Ajax 輪詢方案有極大的性能優勢;而在開發方面,也十分簡單,我們只需要實例化WebSocket,創建連接,查看是否連接成功,然後就可以發送和相應消息了。我們會在後面的實例中去詳細的說明API。 

四、搭建WebSocket服務器

其實,在服務器的選擇上很廣,基本上,主流語言都有WebSocket的服務器端實現,如果作爲前端開發工程師,當然要選擇現在比較火熱的NodeJS作爲我們的服務器端環境了。

NodeJS本身並沒有原生的WebSocket支持,但是有第三方的實現(大家要是有興趣的話,完全可以參考WebSocket協議來做自己的實現),我們選擇了“ws”作爲我們的服務器端實現。本文的重點是講解WebSocket,所以,對於NodeJS不做過多的介紹,不太熟悉的朋友可以去參考NodeJS入門指南(http://www.nodebeginner.org/index-zh-cn.html)。

安裝好NodeJS之後,我們需要安裝“ws”,也就是我們的WebSocket實現,安裝方法很簡單,在終端或者命令行中輸入:

npm install ws

,等待安裝完成就可以了。

接下來,我們需要啓動我們的WebSocket服務。首先,我們需要構建自己的HTTP服務器,在NodeJS中構建一個簡單的HTTP服務器很簡單,so easy。代碼如下:

var app = http.createServer(onRequest ).listen( 8888 );

onRequest()作爲回調函數,它的作用是處理請求,然後做出響應,實際上就是根據接收的URL,在服務器上查找相應的資源,最終返回給瀏覽器。

在構建了HTTP服務器後,我們需要啓動WebSocket服務,代碼如下:

var WebSocketServer = require('ws').Server;

var wss = new WebSocketServer( { server : app } );

從代碼中可以看出,在初始化WebSocket服務時,把我們剛纔構建好的HTTP實例傳遞進去就好。到這裏,服務端代碼差不多也就編寫完成了。怎麼樣?很簡單吧。

五、WebSocket API

上面我們介紹了WebSocket服務端的知識,接下來,我們需要編寫客戶端代碼了。在前面我們說過,客戶端的API也是一如既往的簡單:

見上圖:ready state中定義的是socket的狀態,分爲connection、open、closing和closed四種狀態,從字面上就可以區分出它們所代表的狀態。

上圖描述的是WebSocket的事件,分爲onopen、onerror和onclose;

上圖爲消息的定義,主要是接收和發送消息。注意:可以發送二進制的數據。

以上個圖的具體的含義就不再一一贅述,詳細描述請參考:http://www.w3.org/TR/2012/WD-websockets-20120524/

PS:由於WebSocket API(截止到2012年7月)還是草案,API文檔和上文所描述的會有所不同,請以官方文檔爲主,這也是我爲什麼不詳細描述API中各個屬性的原因。

另外一點需要提醒大家的是:在前端開發中,瀏覽器兼容是必不可少的,而WebSocket在主瀏覽器中的兼容還是不錯的,火狐和Chrome不用說,最新版的支持非常不錯,而且支持二進制數據的發送和接收。但是IE9並不支持,對於國內的大多數應用場景,WebSocket無法大規模使用。

截圖來自(http://tongji.baidu.com/data/browser),之所以選擇百度的統計數據,是因爲更加符合國內的實際情況。圖中所展示的是2012年4月1日到2012年6月30日之間的統計數據,從圖中不難看出IE6.0、奇虎360、IE7.0和IE8.0加起來一共佔據了77%的市場,FireFox屬於其他,chrome只有5.72%的份額,再一次告訴我們,我們的主戰場依然是IE系。

既然是IE系,那麼對於WebSocket在實際app中的應用就基本不可能了。但我們完全可以在chrome、FireFox、以及移動版的IOS瀏覽器中使用它。

六、實例解析

搭建好了服務端,熟悉了API,接下來,我們要開始構建我們的應用了。鑑於WebSocket自身的特點,我們的第一個demo選擇了比較常見的聊天程序,我們暫且取名爲chat。

說到聊天,大家最先想到的肯定是QQ,沒錯,我們所實現的應用和QQ類似,而且還是基於web的。因爲是demo,我們的功能比較簡陋,僅實現了最簡單的會話功能。就是啓動WebSocket服務器後,客戶端發起連接,連接成功後,任意客戶端發送消息,都會被服務器廣播給所有已連接的客戶端,包括自己。

既然需要客戶端,我們需要構建一個簡單的html頁面,頁面中樣式和元素,大家可以自由發揮,只要能夠輸入消息,有發送按鈕,最後有一個展示消息的區域即可。具體的樣子大家可以看附件中的demo。

寫玩HTML頁面之後,我們需要添加客戶端腳本,也就是和WebSocket相關的代碼;前面我們說過,WebSocket的API本身很簡單,所以,我們的客戶端代碼也很直接,如下:

var wsServer = 'ws://localhost:8888/';

var websocket = new WebSocket(wsServer);

websocket.binaryType = "arraybuffer";

websocket.onopen = onOpen;

websocket.onclose = onClose;

websocket.onmessage = onMessage;

websocket.onerror = onError;

首先,我們需要指定WebSocket的服務地址,也就是var wsServer = 'ws://localhost:8888/';

然後,我們實例化WebSocket,new WebSocket(wsServer),

onmessage()這個回調函數會在客戶端收到消息時觸發,也就是說,只要服務器端發送了消息,我們就可以通過onmessage拿到發送的數據,既然拿到了數據,接下去該怎麼玩,就隨便我們了。請看下面的僞代碼:剩下的就是指定相應的回調函數了,分別是onOpen,onClose,onMessage和onError,對於咱們的實驗應用來說,onopen、onclose、onerror甚至可以不管,咱們重點關注一下onmessage。

function onMessage(evt) {

 var json = JSON.parse(evt.data);

 commands[json.event](json.data);

}

因爲onmessage只接收字符串和二進制類型的數據,如果需要發送json格式的數據,就需要我們轉換一下格式,把字符串轉換成JSON格式。只要是支持WebSocket,肯定原生支持window.JSON,所以,我們可以直接使用JSON.parse()和JSON.stringify()來進行轉換。

轉換完成後,我們就得到了我們想要的數據了,接下來所做的工作就是將消息顯示出來。實際上就是

Elements.innerHTML += data + '</br>';

上面展現了客戶端的代碼,服務器端的代碼相對要簡單一些,因爲我們的服務器端使用的是第三方實現,我們只需要做一些初始化工作,然後在接收到消息時,將消息廣播出去即可,下面是具體的代碼:

var app = http.createServer( onRequest ).listen( 8888 );

var WebSocketServer = require('ws').Server,

 wss = new WebSocketServer( { server : app } );

wss.on('connection', function( ws ) {

 console.log('connection successful!');

 ws.on('message', function( data, flags ) {

 console.log(data);

 //do something here

 });

 ws.on('close', function() {

 console.log('stopping client');

 });

});

我們可以通過wss.clients獲得當前已連接的所有客戶端,然後遍歷,得到實例,調用send()方法發送數據;

var clients = wss.clients, len = clients.length, i = 0;

 for( ; i <len; i = i + 1 ){

 clients[i].send( msg );

 }

說到這裏,一個雙向通信的實例基本完成,當然,上面都是僞代碼,完整的demo請查看附件。

除了常見的聊天程序以外,大家完全可以發揮創意,構建一些“好玩”的應用;

接下來,分享另外一個應用,“你畫我猜”這個應用,很多人都接觸過,大致上是:某個人在屏幕上畫一些圖形,這些圖片會實時展示在其它人的屏幕上,然後來猜畫的是什麼。

利用WebSocket和canvas,我們可以很輕鬆的構建類似的應用。當然,我們這裏只是demo,並沒有達到產品級的高度,這裏只是爲大家提供思路;

首先,我們再次明確一下,WebSocket賦予了我們在瀏覽器端和服務器進行雙向通信的能力,這樣,我們可以實時的將數據發送給服務器,然後再廣播給所有的客戶端。這和聊天程序的思路是一致的。

接下來,服務器端的代碼不用做任何修改,在html頁面中準備一個canvas,作爲我們的畫布。如何在canvas上用鼠標畫圖形呢?我們需要監聽mousedown、mousemove和mouseup三個鼠標事件。說到這裏,大家應該知道怎麼做了。沒錯,就是在按下鼠標的時候,記錄當前的座標,移動鼠標的時候,把座標發送給服務器,再由服務器把座標數據廣播給所有的客戶端,這樣就可以在所有的客戶端上同步繪畫了;最後,mouseup的時候,做一些清理工作就ok了。下面是一些僞代碼:

var WhiteBoard = function( socket, canvasId ){
    var lastPoint = null,
     mouseDown = false,
     canvas = getById(canvasId),
     ctx = canvas.getContext('2d');

    var handleMouseDown = function(event) {
     mouseDown = true;
     lastPoint = resolveMousePosition.bind( canvas, event )();
    };

    var handleMouseUp = function(event) {
     mouseDown = false;
     lastPoint = null;
    };

    var handleMouseMove = function(event) {
     if (!mouseDown) { return; }
     var currentPoint = resolveMousePosition.bind( canvas, event )();
     socket.send(JSON.stringify({
      event: 'draw',
      data: {
       points: [
        lastPoint.x,
        lastPoint.y,
        currentPoint.x,
        currentPoint.y
       ]
      }
     }));

     lastPoint = currentPoint;
    };   

    var init = function(){
     addEvent( canvas, 'mousedown', handleMouseDown );
     addEvent( canvas, 'mouseup', handleMouseUp );
     addEvent( canvas, 'mousemove', handleMouseMove );

     var img = new Image();
     addEvent( img, 'load', function(e){
      canvas.width = img.width;
      canvas.height = img.height;
      ctx.drawImage( img, 0, 0 );
     } );
     img.src = '/img/diablo3.png';
    };

    var drawLine = function(data) {
     var points = data.points;
     ctx.strokeStyle = 'rgb(255, 15, 255)';
     ctx.beginPath();
     ctx.moveTo( points[0] + 0.5, points[1] + 0.5 );
     ctx.lineTo( points[2] + 0.5, points[3] + 0.5 );
     ctx.stroke();
    };

    function resolveMousePosition(event) {
     var x, y;
     if (event.offsetX) {
      x = event.offsetX;
      y = event.offsetY;
     } else {  //(注意)實際開發中,這樣獲取鼠標相對canvas的座標是不對的
      x = event.layerX - this.offsetLeft;
      y = event.layerY - this.offsetTop;
     }
     return { x: x, y: y };
    };

    init();

    return {
     draw : drawLine
     //ctx : ctx,
     //canvas : canvas
    }
}( websocket, 'drawsomething' );

對於canvas不熟悉的同學,請自己去搜索一下,有許多不錯的教程。其它方面,和聊天應用的思路基本一樣。

最後,我們需要明確一點,WebSocket本身的優點很明顯,但是作爲一個正在演變中的web規範,我們必須清楚的認識到WebSocket在構建應用時的一些風險;雖然本身有很多侷限性,但是這項技術本身肯定是大勢所趨,WebSocket在移動終端,在chrome web store都有用武之地,我們可以進行大膽的嘗試,讓我們在技術的革新中不被淘汰。

Resources:

W3 API的官方文檔,有詳細的接口設計文檔和實現步驟http://www.w3.org/TR/websockets/

http://tools.ietf.org/html/rfc6455

WebSocket協議

http://tools.ietf.org/html/rfc6202

Known Issues and Best Practices for the Use of Long Polling and Streaming in Bidirectional HTTP

http://msdn.microsoft.com/en-us/library/ie/hh673567(v=vs.85).aspx

msdn中關於WebSocket的介紹

https://developer.mozilla.org/en/WebSockets

http://caniuse.com/#feat=websockets

Compatibility tables for support of HTML5, CSS3, SVG and more in desktop and mobile browsers.





發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章