WebSocket科普

  WebSocket为何物?如果你现在还不太清楚请先到baidu百科一下。如果你实在懒得起搜索的话,下面帮你从网络上搜罗了些许有关WebSocket的信息。

  Baidu百科:http://baike.baidu.com/view/3623887.htm (下面摘录了些许内容,内容来自Baidu)

  WebSocket 规范的目标是在浏览器中实现和服务器端双向通信.双向通信可以拓展浏览器上的应用类型,例如实时的数据推送(股票行情),游戏,聊天/im 等.

  背景:目前在浏览器中通过http仅能实现单向的通信,comet可以一定程度上模拟双向通信,但效率较低,并需要服务器有较好的支持; flash中的socket和xmlsocket可以实现真正的双向通信,通过 flex ajax bridge,可以在javascript中使用这两项功能. 可以预见,如果websocket一旦在浏览器中得到实现,将会替代上面两项技术,得到广泛的使用.面对这种状况,HTML5定义了WebSocket协议,能更好的节省服务器资源和带宽并达到实时通讯。

原理:websocket规范由两部分组成,一部分是浏览器中的 websocket api, 由w3c 制订, 一部分是websocket 协议, 由ietf制订,目前是draft状态.websocket的协议比较简单, 客户端和普通的浏览器一样通过80或者443端口和服务器进行请求握手,服务器根据http header识别是否一个websocket请求,如果是,则将请求升级为一个websocket连接,握手成功后就进入双向长连接的数据传输阶段. websocket的数据传输是基于帧的方式: 0x00 表示数据开始, 0xff表示数据结束,数据以utf-8编码.


握手协议:

在实现websocket连线过程中,需要透过浏览器发出websocket连线请求,然后服务器发出回应,这个过程通常称为“握手” (handshaking)。

  PS1:握手协议在后期的版本中,会标明版本编号,下面的例子属于早期的协定之一,对于新版的 chrome 和 Firefox 皆不适用。

  PS2:后期的版本大多属于功能上的扩充,例如使用第7版的握手协议同样也适用于第8版的握手协议。

  例子:

  浏览器请求

  GET /demo HTTP/1.1

  Host: 你的网址.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://你的网址.com

  ^n:ds[4U

  服务器回应

  HTTP/1.1 101

  WebSocket Protocol Handshake

  Upgrade: WebSocket

  Connection: Upgrade

  Sec-WebSocket-Origin: http://你的网址.com

  Sec-WebSocket-Location: ws://你的网址.com/demo

  Sec-WebSocket-Protocol: sample

  8jKS’y:G*Co,Wxa-


支持的浏览器


  目前实现了websocket的浏览器:

  

Chrome Supported in version 4+

Firefox Supported in version 4+

Internet Explorer Supported in version 10+

Opera Supported in version 10+

Safari Supported in version 5+


支持的服务器


  在服务器端,也出现了一些实现websocket协议的项目:

  jetty 7.0.1 包含了一个初步的实现

  resin 包含有websocket 实现

  pywebsocket, apache http server 扩展

  apache tomcat 7.0.27 版本

  websocket api在浏览器端的广泛实现似乎只是一个时间问题了, 值得注意的是目前服务器端没有标准的api, 各个实现都有自己的一套api, 并且jcp也没有类似的提案, 所以使用websocket开发服务器端有一定的风险.可能会被锁定在某个平台上或者将来被迫升级.


  Baidu百科对Websocket做了基本的介绍。WebSocket是html5的一部分,而html5标准还处于制定过程中。根据网上的信息得知到2014年底,HTML5才将成为一种完整的成品标准(来自 http://www.iteye.com/news/26113),W3C计划到2016年底发布后续版本HTML 5.1。而且 新的HTML 5.1将包含较少的技术,曾经包含在HTML 5之下的Web Workers 和WebSockets现在都将成为单独标准。可见websocket还处于一个快速发展变化的过程中。

  从WebSocket的协议的握手处理和数据传输格式的快速变化可窥其一斑。WebSocket协议握手处理和消息格式的变迁(来自网络:http://lchshu001.iteye.com/blog/1184428

1.握手协议 
版本0--3中: 
握手通过请求头Sec-WebSocket-Key1 和 Sec-WebSocket-Key2 的值和 8 字节的请求实体,进行MD5加密,将加密结果,构造出一个16字节作为请求实体的内容返回。如下实例: 
------------------请求-------------------------------------------- 

  1. GET /demo HTTP/1.1  
  2. Host: example.com  
  3. Connection: Upgrade  
  4. Sec-WebSocket-Key2: 12998 5 Y3 1  .P00  
  5. Sec-WebSocket-Protocol: sample  
  6. Upgrade: WebSocket  
  7. Sec-WebSocket-Key1: 4 @1  46546xW%0l 1 5  
  8. Origin: http://example.com  
  9. (\r\n)  
  10. ^n:ds[4U  


------------------响应-------------------------------------------- 

  1. HTTP/1.1 101 WebSocket Protocol Handshake  
  2. Upgrade: WebSocket  
  3. Connection: Upgrade  
  4. Sec-WebSocket-Origin: http://example.com  
  5. Sec-WebSocket-Location: ws://example.com/demo  
  6. Sec-WebSocket-Protocol: sample  
  7. (\r\n)  
  8. 8jKS'y:G*Co,Wxa-  


------------------------------------------------------------------ 

把第一个Key中的数字除以第一个Key的空白字符的数量,而第二个Key也是如此,这样得到两个整数,把每个整数写的四个字节里去,串为8个字节,然后和请求实体里面的8个字节串为16字节,将这16个字节进行MD5加密(如实例中的结果:8jKS'y:G*Co,Wxa-),得到一个16字节的数据作为响应实体的内容,返回给客户端,这样握手成功。 


在版本4之后,握手协议修改了: 
------------------请求-------------------------------------------- 

  1. GET /chat HTTP/1.1  
  2. Host: server.example.com  
  3. Upgrade: websocket  
  4. Connection: Upgrade  
  5. Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==  
  6. Sec-WebSocket-Origin: http://example.com  
  7. Sec-WebSocket-Protocol: chat, superchat  
  8. (\r\n)  



------------------响应-------------------------------------------- 

  1. HTTP/1.1 101 Switching Protocols  
  2. Upgrade: websocket  
  3. Connection: Upgrade  
  4. Sec-WebSocket-Accept: me89jWimTRKTWwrS3aRrL53YZSo=  
  5. Sec-WebSocket-Nonce: AQIDBAUGBwgJCgsMDQ4PEC==  
  6. Sec-WebSocket-Protocol: chat  



使用请求头的值 Sec-WebSocket-Key,该值是BASE-64编码(base64-encoded)的,我们不需要转码,加上一个魔幻字符串: "258EAFA5-E914-47DA-95CA-C5AB0DC85B11",(结果:[dGhlIHNhbXBsZSBub25jZQ==258EAFA5-E914-47DA-95CA-C5AB0DC85B11])使用 SHA-1 加密,之后进行 BASE-64编码,将结果做为 Sec-WebSocket-Accept 头的值,返回给客户端。 
如果服务器端有 Sec-WebSocket-Nonce 头,表示要在Sec-WebSocket-Key 的值,和魔幻字符串之间加入该 Sec-WebSocket-Nonce 头的值,即“dGhlIHNhbXBsZSBub25jZQ==AQIDBAUGBwgJCgsMDQ4PEC==258EAFA5-E914-47DA-95CA-C5AB0DC85B11”,进行 SHA-1 加密,之后和前面的相同。完成握手协议。 

2.消息帧格式

在版本 0 中, 数据帧比较的简单。数据帧以 0x00 开头,以0xFF结尾,中间的数据以utf-8编码的字符就可以了。当然这个简单的格式只能用来传输字符串。无法传输字节流。所以 版本 1 就做了修改了,后面的版本绝大部分是兼容的。 
后面的这个帧结构就有点复杂了,如下所示(一行是4个字节,32 bit): 

  1.  0                   1                   2                   3  
  2.  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1  
  3. +-+-+-+-+-------+-+-------------+-------------------------------+  
  4. |M|R|R|R| opcode|R| Payload len |    Extended payload length    |  
  5. |O|S|S|S|  (4)  |S|     (7)     |             (16/63)           |  
  6. |R|V|V|V|       |V|             |   (if payload len==126/127)   |  
  7. |E|1|2|3|       |4|             |                               |  
  8. +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +  
  9. |     Extended payload length continued, if payload len == 127  |  
  10. + - - - - - - - - - - - - - - - +-------------------------------+  
  11. |                               |         Extension data        |  
  12. +-------------------------------+ - - - - - - - - - - - - - - - +  
  13. :                                                               :  
  14. +---------------------------------------------------------------+  
  15. :                       Application data                        :  
  16. +---------------------------------------------------------------+  


[MORE] 表示一个数据通过多个帧进行传输, 如果是 0 表示后面还有数据帧,如果是 1 则表示是最后一个帧。 
[RSV1][RSV2][RSV3][RSV4] 未做定义暂时全为零。 
[opcode] 标识数据的格式,以及帧的控制,如:08标识数据内容是 文本,01标识:要求远端去关闭当前连接。 
[Payload len] 如果小于126 表示后面的数据长度是 [Payload len] 的值。(最大125byte) 
              等于 126 表示之后的16 bit位的数据值标识数据的长度。(最大65535byte) 
              等于 127 表示之后的64 bit位的数据值标识数据的长度。(一个有符号长整型的最大值) 
[Extension data]没有提及怎么使用。 
[Application data] 为应用提供的数据。 

版本7之后,添加了 MASK 的概念。相当于对数据加密。而且要求客户端必须是MASK的。 

  1.  0                   1                   2                   3  
  2.  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1  
  3. +-+-+-+-+-------+-+-------------+-------------------------------+  
  4. |F|R|R|R| opcode|M| Payload len |    Extended payload length    |  
  5. |I|S|S|S|  (4)  |A|     (7)     |             (16/63)           |  
  6. |N|V|V|V|       |S|             |   (if payload len==126/127)   |  
  7. | |1|2|3|       |K|             |                               |  
  8. +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +  
  9. |     Extended payload length continued, if payload len == 127  |  
  10. + - - - - - - - - - - - - - - - +-------------------------------+  
  11. |                               |Masking-key, if MASK set to 1  |  
  12. +-------------------------------+-------------------------------+  
  13. | Masking-key (continued)       |          Payload Data         |  
  14. +-------------------------------- - - - - - - - - - - - - - - - +  
  15. :                     Payload Data continued ...                :  
  16. + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +  
  17. |                     Payload Data continued ...                |  
  18. +---------------------------------------------------------------+  


[opcode]  01标识数据内容是 文本,08标识 : 要求远端去关闭当前连接。 
[MASK](即原先的RSV4)如果是 1 则数据是被 MASK 的。 
[Masking-key] 如果MASK为 1 则有4字节的 Masking-key,用于与传输的数据 [Payload Data] 进行异或运算,4byte(32bit)进行一次运算,不足四位从前往后对应,如只有三位,则只与[Masking-key]的前三位进行运算。 

 

3.关闭远端连接:
在版本 0 时:传两个字节 (0xff,0x00); 
在版本 1--6 时:传三个字节 (0x80,0x01,0x00); 
在版本 7--以上 时:传两个字节 (0x88,0x00); 
经测试 只有 在版本 7--以上 时:传两个字节 (0x88,0x00)。

  以上 WebSocket握手协议处理,消息格式和关闭远端的变迁摘录自网络。

Websocket协议草案:http://tools.ietf.org/html/rfc6455 ;如果你对自己的E文有足够的信心,有足够的时间你可以去阅读翻译WebSocket协议。协议草案版本更新的很频繁。

尽管Websocket协议还处于一个快速进化发展的过程中,但我们不能等到他完全成熟之后再去学习使用他。因为移动互联网时代的道来,我们真的很需要他。

Html5支持的WebSocket AIP:(一下内容摘自:http://edu.cnzz.cn/Webmaster/Site/2/2012/0807/31898.html)

WebSocket 的js 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不用说,最新版的支持非常不错,而且支持二进制数据的发送和接收。

下面给出一个html中的实例:(一下内容摘自:http://www.sztaixie.com/blackie/?p=123

下面的代码片段是打开一个连接,为连接创建事件监听器,断开连接,消息时间,发送消息返回到服务器,关闭连接。

// 创建一个Socket实例
var socket = new WebSocket(‘ws://localhost:8080′); 

 

// 打开Socket
socket.onopen = function(event) {

// 发送一个初始化消息
socket.send(‘I am the client and I\’m listening!’);

// 监听消息
socket.onmessage = function(event) {
console.log(‘Client received a message’,event);
};

// 监听Socket的关闭
socket.onclose = function(event) {
console.log(‘Client notified socket has closed’,event);
};

// 关闭Socket….
//socket.close()
};

让我们来看看上面的初始化片段。参数为URL,ws表示WebSocket协议。onopen、onclose和onmessage方法把事件连接到Socket实例上。每个方法都提供了一个事件,以表示Socket的状态。

onmessage事件提供了一个data属性,它可以包含消息的Body部分。消息的Body部分必须是一个字符串,可以进行序列化/反序列化操作,以便传递更多的数据。

WebSocket的语法非常简单,使用WebSockets是难以置信的容易……除非客户端不支持WebSocket。

此文针对网络上的websocket资料进行了一些整理以方便将要学习研究Websocket的同学。

猜你喜欢

转载自cshbbrain.iteye.com/blog/1685591