亚洲免费在线-亚洲免费在线播放-亚洲免费在线观看-亚洲免费在线观看视频-亚洲免费在线看-亚洲免费在线视频

WebSocket實戰

系統 2077 0

前言

互聯網發展到現在,早已超越了原始的初衷,人類從來沒有像現在這樣依賴過他;也正是這種依賴,促進了互聯網技術的飛速發展。而終端設備的創新與發展,更加速了互聯網的進化;

?

HTTP/1.1規范發布于1999年,同年12月24日,HTML4.01規范發布;盡管已到2012年,但HTML4.01仍是主流;雖然 HTML5的草案已出現了好幾個年頭,但轉正日期,遙遙無期,少則三五年,多則數十年;而HTML5的客戶代理(對于一般用戶而言,就是瀏覽器),則已百 家爭鳴,星星向榮;再加上移動終端的飛速發展,在大多數情況下,我們都可以保證擁有一個HTML5的運行環境,所以,我們來分享一下HTML5中的 WebSocket協議;

本文包含以下六個方面:
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在移動終端上的死亡。

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

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

?

接下來,我們再來看一下WebSocket,為什么要使用它呢?高效節能,簡單易用。
下圖是來自websocket.org的測試結果:
WebSocket實戰

在流量和負載增大的情況下,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也是一如既往的簡單:
WebSocket實戰
見上圖:ready state中定義的是socket的狀態,分為connection、open、closing和closed四種狀態,從字面上就可以區分出它們所代表的狀態。

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

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

?

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


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

?

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

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),
剩下的就是指定相應的回調函數了,分別是onOpen,onClose,onMessage和onError,對于咱們的實驗應用來說,onopen、onclose、onerror甚至可以不管,咱們重點關注一下onmessage。

onmessage()這個回調函數會在客戶端收到消息時觸發,也就是說,只要服務器端發送了消息,我們就可以通過onmessage拿到發送的數據,既然拿到了數據,接下去該怎么玩,就隨便我們了。請看下面的偽代碼:

1 function onMessage(evt) {
2 ???? var json = JSON.parse(evt.data);
3 ???? commands[json.event](json.data);
4 }

因為onmessage只接收字符串和二進制類型的數據,如果需要發送json格式的數據,就需要我們轉換一下格式,把字符串轉換成JSON格式。 只要是支持WebSocket,肯定原生支持window.JSON,所以,我們可以直接使用JSON.parse()和 JSON.stringify()來進行轉換。
轉換完成后,我們就得到了我們想要的數據了,接下來所做的工作就是將消息顯示出來。實際上就是

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

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

01 var app = http.createServer( onRequest ).listen( 8888 );
02 var WebSocketServer = require('ws').Server,
03 ???? wss = new WebSocketServer( { server : app } );
04 wss.on('connection', function( ws ) {
05 ???? console.log('connection successful!');
06 ???? ws.on('message', function( data, flags ) {
07 ???????? console.log(data);
08 ???????? //do something here
09 ???? });
10 ???? ws.on('close', function() {
11 ???????? console.log('stopping client');
12 ???? });
13 });

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

1 var clients = wss.clients, len = clients.length, i = 0;
2 ???????? for( ; i < len; i = i + 1 ){
3 ???????????? clients[i].send( msg );
4 ???????? }

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

除了常見的聊天程序以外,大家完全可以發揮創意,構建一些“好玩”的應用;
接下來,分享另外一個應用,“你畫我猜”這個應用,很多人都接觸過,大致上是:某個人在屏幕上畫一些圖形,這些圖片會實時展示在其它人的屏幕上,然后來猜畫的是什么。

利用WebSocket和canvas,我們可以很輕松的構建類似的應用。當然,我們這里只是demo,并沒有達到產品級的高度,這里只是為大家提供思路;
首先,我們再次明確一下,WebSocket賦予了我們在瀏覽器端和服務器進行雙向通信的能力,這樣,我們可以實時的將數據發送給服務器,然后再廣播給所有的客戶端。這和聊天程序的思路是一致的。

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

01 var WhiteBoard = function( socket, canvasId ){
02 ???????????????? var lastPoint = null,
03 ???????????????????? mouseDown = false,
04 ???????????????????? canvas = getById(canvasId),
05 ???????????????????? ctx = canvas.getContext('2d');
06 ?
07 ???????????????? var handleMouseDown = function(event) {
08 ???????????????????? mouseDown = true;
09 ???????????????????? lastPoint = resolveMousePosition.bind( canvas, event )();
10 ???????????????? };
11 ?
12 ???????????????? var handleMouseUp = function(event) {
13 ???????????????????? mouseDown = false;
14 ???????????????????? lastPoint = null;
15 ???????????????? };
16 ?
17 ???????????????? var handleMouseMove = function(event) {
18 ???????????????????? if (!mouseDown) { return; }
19 ???????????????????? var currentPoint = resolveMousePosition.bind( canvas, event )();
20 ???????????????????? socket.send(JSON.stringify({
21 ???????????????????????? event: 'draw',
22 ???????????????????????? data: {
23 ???????????????????????????? points: [
24 ???????????????????????????????? lastPoint.x,
25 ???????????????????????????????? lastPoint.y,
26 ???????????????????????????????? currentPoint.x,
27 ???????????????????????????????? currentPoint.y
28 ???????????????????????????? ]
29 ???????????????????????? }
30 ???????????????????? }));
31 ?
32 ???????????????????? lastPoint = currentPoint;
33 ???????????????? };?????????
34 ?
35 ???????????????? var init = function(){
36 ???????????????????? addEvent( canvas, 'mousedown', handleMouseDown );
37 ???????????????????? addEvent( canvas, 'mouseup', handleMouseUp );
38 ???????????????????? addEvent( canvas, 'mousemove', handleMouseMove );
39 ?
40 ???????????????????? var img = new Image();
41 ???????????????????? addEvent( img, 'load', function(e){
42 ???????????????????????? canvas.width = img.width;
43 ???????????????????????? canvas.height = img.height;
44 ???????????????????????? ctx.drawImage( img, 0, 0 );
45 ???????????????????? } );
46 ???????????????????? img.src = '/img/diablo3.png';
47 ???????????????? };
48 ?
49 ???????????????? var drawLine = function(data) {
50 ???????????????????? var points = data.points;
51 ???????????????????? ctx.strokeStyle = 'rgb(255, 15, 255)';
52 ???????????????????? ctx.beginPath();
53 ???????????????????? ctx.moveTo( points[0] + 0.5, points[1] + 0.5 );
54 ???????????????????? ctx.lineTo( points[2] + 0.5, points[3] + 0.5 );
55 ???????????????????? ctx.stroke();
56 ???????????????? };
57 ?
58 ???????????????? function resolveMousePosition(event) {
59 ???????????????????? var x, y;
60 ???????????????????? if (event.offsetX) {
61 ???????????????????????? x = event.offsetX;
62 ???????????????????????? y = event.offsetY;
63 ???????????????????? } else {? //(注意)實際開發中,這樣獲取鼠標相對canvas的坐標是不對的
64 ???????????????????????? x = event.layerX - this.offsetLeft;
65 ???????????????????????? y = event.layerY - this.offsetTop;
66 ???????????????????? }
67 ???????????????????? return { x: x, y: y };
68 ???????????????? };
69 ?
70 ???????????????? init();
71 ?
72 ???????????????? return {
73 ???????????????????? draw : drawLine
74 ???????????????????? //ctx : ctx,
75 ???????????????????? //canvas : canvas
76 ???????????????? }
77 ???????????? }( websocket, 'drawsomething' );

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

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

Resources:
http://www.w3.org/TR/websockets/
W3 API的官方文檔,有詳細的接口設計文檔和實現步驟
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.

?

原文: ? WebSocket實戰 by Ji Yunpeng / 前端開發

?

?

?

WebSocket實戰


更多文章、技術交流、商務合作、聯系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號聯系: 360901061

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對您有幫助就好】

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長會非常 感謝您的哦!??!

發表我的評論
最新評論 總共0條評論
主站蜘蛛池模板: 国产成人综合久久亚洲精品 | 亚洲欧美中文日韩二区一区 | 久久精品国产国产精品四凭 | 日本在线不卡免 | 亚洲国产精品激情在线观看 | 久久综合九色综合97_ 久久久 | 国产日本久久久久久久久婷婷 | 亚洲人xx视频 | 亚洲大片在线观看 | 99精品视频在线视频免费观看 | 黄色片在线免费观看视频 | 99久久精品国产亚洲 | 中国性xxxxx极品奶水 | 四虎国产成人永久精品免费 | 全免费午夜一级毛片一级毛 | 久久2| 亚洲久久网 | 亚洲综合网站久久久 | 免费一级毛片清高播放 | 五月国产综合视频在线观看 | 欧美成人鲁丝片在线观看 | 国产资源精品一区二区免费 | 免费在线观看的毛片 | 美女一级毛片视频 | 欧美日韩国产欧美 | 三区在线观看 | 国产成人综合久久综合 | 成人午夜爽爽爽免费视频 | 91精品全国免费观看老司机 | 亚洲欧美日韩中文综合在线不卡 | 日本二级毛片免费 | 久草在线久草在线 | 日韩国产欧美成人一区二区影院 | 国产精品一久久香蕉产线看 | 香蕉视频免费在线观看 | 国产成人精品一区二区三在线观看 | 欧美亚洲日本在线 | 在线观看久久 | 操熟美女又肥又嫩的骚屁股 | 在线观看日本中文字幕 | 91日本在线精品高清观看 |