应用 HTML 5 的 WebSocket 实现 BiDirection 数据交换

WebSocket 的优势:

  1. 它可以实现真正的实时数据通 信。众所周知,B/S 模式下应用的是 HTTP 协议,是无状态的,所以不能保持持续的链接。数据交换是通过客户端提交一个 Request 到服务器端,然后服务器端返回一个 Response 到客户端来实现的。而 WebSocket 是通过 HTTP 协议的初始握手阶段然后升级到 Web Socket 协议以支持实时数据通信。
  2. WebSocket 可以支持服务器主动向客户端推送数据。一旦服务器和客户端通过 WebSocket 建立起链接,服务器便可以主动的向客户端推送数据,而不像普通的 web 传输方式需要先由客户端发送 Request 才能返回数据,从而增强了服务器的能力。
  3. WebSocket 协议设计了更为轻量级的 Header,除了首次建立链接的时候需要发送头部和普通 web 链接类似的数据之外,建立 WebSocket 链接后,相互沟通的 Header 就会异常的简洁,大大减少了冗余的数据传输。

WebSocket 提供了更为强大的通信能力和更为简洁的数据传输平台,能更为方便的完成 Web 开发中的双向通信功能。
WebSocket 和 Ajax 的技术实现和流量分析
普通 HTTP 请求方式
一 般情况下,通过浏览器访问一个网页,需要浏览器发送一个 HTTP Request,服务器接收到浏览器的请求,返回相应的消息。在一些数据更新比较频繁的应用里,页面的数据要想得到最新的结果需要重新刷新页面,但这样会 产生大量的冗余数据在服务器和客户端传输,另外由于页面是同步处理的,所以在页面加载完毕之前是不能继续操作的。这样会阻塞用户的动作,显然不是一个好的 解决方案。
异步传输方式
随着技术发展,后来出现了新的技 术方案,即 Ajax 技术。Ajax 全称是 Asynchronous JavaScript and XML,即异步 JavaScript 和 XML。它的核心是 XMLHttpRequest 技术,通过 XMLHttpRequest 对象可以向服务器提交异步请求,服务器可以将数据以异步方式返回给客户端,通过 Javascript 局部的更新页面,不会阻塞当前用户操作,这样的解决方案相对于前面的来说用户体验提高了很多。这样可以通过 JavaScript 使客户端不断地向服务器发送异步请求,并用返回的数据刷新局部页面来达到“实时”的数据更新。
但是这样的方案也有问题,因为有些情况下,服 务器端的数据更新间隔我们是不能预知的,通常我们都是在客户端设定一定的时间间隔去服务端请求数据,即所谓的轮询技术。但是如果服务端数据的更新间隔小于 我们设定的频率,那么就会有一些数据取不到。而如果服务端数据的更新间隔大于我们设定的频率,那么就会有冗余的数据传输。
长轮询技术
鉴于这些缺点,有一些改进的方案应运而生,Comet 就是其中的代表。
Comet 技术被称为(long-polling)长轮询技术,它改变了服务器和客户端的交互方式的。首先由客户端发出请求,服务器接收到请求后并不一定立即返回,而是等到有数据更新时才返回或者直到连接超时。这样就不会出现冗余的数据请求。

图 1. Comet 技术工作方式

通常,为了模拟基于半双工 HTTP 上的全双工通信,目前的许多解决方案都使用了两个连接:一个下行连接,一个上行连接。一方面,维护和协调这两个连 接需要大量的系统开销,并增加了复杂性。另一方面,还给网络负载带来了很大压力。
HTTP 请求数据
下面是一次 Ajax 请求的传输数据:

清单 1. HTTP 请求

双击代码全选

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

var worker = new Worker(dedicated.js');   

 Host:www.demo.com   

 User-AgentMozilla/5.0 (Windows NT 5.1; rv:5.0) Gecko/20100101 Firefox/5.0   

 Accept:*/*   

 Accept-Language:en-us,en;q=0.5   

 Accept-Encoding:gzip, deflate   

 Accept-Charset:ISO-8859-1,utf-8;q=0.7,*;q=0.7   

 Connection:keep-alive   

 Referer:http://www.demo.com/news/12365-demo-Chrome   

 Cookie:_application_cookie_id_=1302165217141933;   

 __utma=185941238.840487749.1311923297.1315056086.1315210256.36;   

 __utmz=185941238.1315210256.36.42.utmcsr=search|   

 utmccn=(organic)|utmcmd=organic|utmctr=websocket%20%C0%FD%D7%D3;   

 __utmc=432143214.765498335.268654276.1565587542.4468744325.36;   

 lzstat_uv=37479629961521195708|2366120@796778;   

 ldsetat_ac=54325299615432195708|235432320@74321778;   

 _application3_session_=BAh7BjoPc2Vzc2lvbl9pZCIlMmM2ZTIyYjhmMmQ3   

 ZTUyNDI2NTRlNTc1YzZjOGYwOWY%3D--   

 fb0c80e3bb59c54f4a5080652a6e1f0addccf4e0; __utmc=185941238

 

WebSocket 请求数据
而 WebSocket 与上述方法不同,它首先通过客户端和服务器在初始握手阶段从 HTTP 协议升级到 WebSocket 协议。握手分为两个阶段,首先是客户端请求:

清单 2. WebSocket 请求

双击代码全选

1

2

3

4

5

6

7

8

9

10

worker.onmessage = function (event) { ... };   

GET /demo HTTP/1.1   

Host: example.com   

Connection: Upgrade   

Sec-WebSocket-Key2: 12998 5 Y3 1  .P00   

Sec-WebSocket-Protocol: sample   

Upgrade: WebSocket   

Sec-WebSocket-Key1: 4 @1  46546xW%0l 1 5   

Origin: http://example.com   

^n:ds[4U

 

然后是服务器响应:

清单 3. 服务器响应

双击代码全选

1

2

3

4

5

6

7

HTTP/1.1 101 WebSocket Protocol Handshake   

Upgrade: WebSocket   

Connection: Upgrade   

Sec-WebSocket-Origin: http://example.com   

Sec-WebSocket-Location: ws://example.com/demo   

Sec-WebSocket-Protocol: sample   

8jKS'y:G*Co,Wxa-

 

在这个请求串中,“Sec-WebSocket-Key1”, “Sec-WebSocket-Key2”和最后的“^n:ds[4U”都是随机的,服务器端会用这些数据来构造出一个 16 字节大小的应答结果。
把第一个 Key 中的数字除以第一个 Key 的空白字符的数量,而第二个 Key 也是如此。然后把这两个结果与请求最后的 8 字节字符串连接起来成为一个字符串,服务器应答正文(“8jKS ’ y:G*Co,Wxa-”)即这个字符串的 MD5 sum。
在建立起 WebSocket 连接之后,服务器和客户端的通信消息头就变的非常简洁了,整个消息头只有仅仅两个字节,以“0x00 ″开头以” 0xFF”结尾,中间传输的数据采用 UTF-8 格式。
数据传输量对比
假 设以下场景,如果我们以每秒一次的频率去服务器拉取数据的话,无论每次传输的数据有多少,图 2 中的 HTTP 协议头都必须要传送,这在用户量不太大的情况下还不是太糟糕,但是在多用户的场景下,便会给网络带来的巨大的负载。不同的应用传输的头信息不尽相同,以上 面提到的传输数据为例,假设一次数据请求传输过程中数据头大小为 800Byte,我们来看一下在不同的用户量的条件下,冗余数据的增长情况。

图 2. 数据传输量对比

由 图 2 看以看出,在用户规模大的情况下,情况会变得的愈加恶劣,即使我们把轮询技术改成 comet 技术,也只能减少小部分重复数据的轮询数据,而且 comet 技术还会给服务器带来额外的消耗。所以,使用 WebSocket 技术可以让我们的在大规模的应用场景下减少大量的冗余数据带宽消耗,而且用户规模越大,它所带来的优势就越明显。