`
天梯梦
  • 浏览: 13732752 次
  • 性别: Icon_minigender_2
  • 来自: 洛杉矶
社区版块
存档分类
最新评论

WebSocket实战

 
阅读更多

前言

互联网发展到现在,早已超越了原始的初衷,人类从来没有像现在这样依赖过他;也正是这种依赖,促进了互联网技术的飞速发展。而终端设备的创新与发展,更加速了互联网的进化;

 

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在移动终端上的死亡。

下面是轮询和长轮询的信息流转图:

对比完四种不同的实时通信方式,不难发现,除了基于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),
剩下的就是指定相应的回调函数了,分别是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 / 前端开发

 

 

 

分享到:
评论

相关推荐

    WebSockets:WebSocket实战项目:聊天室应用.docx

    WebSockets:WebSocket实战项目:聊天室应用.docx

    websocket实战源码

    在这篇“WebSocket实战源码”中,我们将会探讨WebSocket的基本原理、实现方式以及通过源码分析来深入理解其工作流程。 WebSocket协议的核心特性在于它能在一次TCP连接上进行多次数据交换,无需像HTTP那样频繁地建立...

    springboot-websocket实战案例

    该项目采用springboot+websocket 实现聊天小案例。 包括 springboot+websocket 实现服务端代码、html5 连接websocket客户端、springboot后台连接websockete客户端代码。

    Golang Websocket实战:深入理解与实现

    Golang,通常被称为Go语言,是一种由谷歌开发的编程语言。它以简洁、高效、安全著称,并且非常适合构建大规模的分布式系统和微服务。Go语言的一些主要特点包括: 1. **简洁性**:Go语言的语法非常简单,易于学习。...

    Flask实战视频教程下载2022

    在本“Flask实战视频教程下载2022”中,你将深入学习如何使用Flask框架,结合Vue.js前端框架以及WebSocket技术,构建一个功能完善的论坛系统与聊天系统。这个教程涵盖了现代Web开发中的重要技术和实践应用,旨在提升...

    WebSocket实战之——JavaScript例子

    从WebSocket通讯三个阶段(打开握手、数据传递、关闭握手)进行了探讨,各阶段中浏览器和服务器做了些什么事情也有所涉及。 二、主要代码解读:1、申请一个WebSocket对象参数是需要连接的服务器端的地址,同http...

    springmvc+websocket demo

    **Spring MVC + WebSocket 实战详解** 在Web开发中,Spring MVC和WebSocket是两种常见的技术,它们各自在不同的场景下发挥着重要作用。Spring MVC是Spring框架的一部分,主要用于构建强大的MVC(Model-View-...

    springboot+websocket进度条实战

    在本项目中,"springboot+websocket进度条实战"是一个基于Spring Boot框架和WebSocket技术的实战应用,旨在展示如何在Web应用程序中实现一个实时的进度条功能。Spring Boot简化了Java Web开发,提供了快速构建独立...

    WebSocket入门实战工程

    总的来说,WebSocket入门实战工程涵盖了网络编程、前后端通信、协议解析等多个方面,对于提升Web应用的实时性能和用户体验具有重要意义。通过实际操作,我们可以深入理解WebSocket的工作原理,进而构建高效、可靠的...

    websocket-demo.zip

    总结来说,"websocket-demo.zip"项目是一个全面的WebSocket实战教程,涵盖了从SpringBoot服务器端配置、WebSocket连接管理到前端页面的交互实现。通过学习和运行这个项目,开发者可以深入理解WebSocket的工作原理...

    SpringAOP+RabbitMQ+WebSocket实战详解

    本文将详细介绍 SpringAOP、RabbitMQ 和 WebSocket 的实战详解,包括 SpringAOP 的基本概念、RabbitMQ 的结构和 WebSocket 的工作原理。 SpringAOP SpringAOP 是一个面向切面编程的框架,主要用来解决系统层面的...

    spring-websocket-portfolio-master.zip

    六、WebSocket实战应用 1. **聊天应用**:通过WebSocket实现实时的消息传递,用户可以即时看到其他人的聊天内容。 2. **实时通知**:在电商、金融等领域,可以利用WebSocket实时推送订单状态、股票价格等更新信息...

    SSM 框架 webSocket的实战

    在本实战项目中,我们将探讨如何在SSM基础上实现WebSocket通信,为Web应用添加实时交互功能。 首先,WebSocket是一种在客户端和服务器之间建立持久连接的协议,允许双方进行全双工通信。在传统的HTTP协议中,每次...

    gin+websocket+mongodb实现 IM 即时聊天系统,基于WS连接的即时聊天

    在本文中,我们将深入探讨如何使用Gin框架、WebSocket技术和MongoDB数据库来构建一个实时的IM(即时消息)聊天系统。这个系统的核心是利用WebSocket提供持久化的双向通信,以实现实时的消息传递。Gin是一个高效的Go...

    python开发实例之python使用Websocket库开发简单聊天工具实例详解(python+Websocket+JS)

    在本实例中,我们将探讨如何使用Python的Websocket库创建一个简单的聊天工具,结合JavaScript作为客户端,实现双向通信。Websocket是一种在客户端和服务器之间建立持久连接的技术,允许双方实时交换数据。 首先,...

    chatroom.rar

    下面将详细讲解如何使用Java来实现WebSocket实战,以及在"chatroom.rar"这个项目中可能涉及的关键技术点。 1. **WebSocket API介绍**: - `@ServerEndpoint`:这个注解标记在WebSocket服务端的类上,定义了...

Global site tag (gtag.js) - Google Analytics