欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

WebSocket科普

程序员文章站 2022-05-04 20:45:36
...

  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是非常的简单:

WebSocket科普
            
    
    博客分类: java综合其他  

见上图:ready state中定义的是socket的状态,分为connection、open、closing和closed四种状态,从字面上就可以区分出它们所代表的状态。

WebSocket科普
            
    
    博客分类: java综合其他  

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

WebSocket科普
            
    
    博客分类: java综合其他  

上图为消息的定义,主要是接收和发送消息。注意:可以发送二进制的数据。

以上个图的具体的含义就不再一一赘述,详细描述请参考: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的同学。