WebSocket

首先认识HTML5的websocket:

在HTML5规范中,我最喜欢的Web技术就是正迅速变得流行的WebSocket API。WebSocket提供了一个受欢迎的技术,以替代我们过去几年一直在用的Ajax技术。这个新的API提供了一个方法,从客户端使用简单的语法有效地推动消息到服务器。让我们看一看HTML5的WebSocket API:它可用于客户端、服务器端。而且有一个优秀的第三方API,名为Socket.IO。

什么是WebSocket API?

WebSocket API是下一代客户端-服务器的异步通信方法。该通信取代了单个的TCP套接字,使用ws或wss协议,可用于任意的客户端和服务器程序。WebSocket目前由W3C进行标准化。WebSocket已经受到Firefox 4、Chrome 4、Opera 10.70以及Safari 5等浏览器的支持。



WebSocket API最伟大之处在于服务器和客户端可以在给定的时间范围内的任意时刻,相互推送信息。WebSocket并不限于以Ajax(或XHR)方式通信,因为Ajax技术需要客户端发起请求,而WebSocket服务器和客户端可以彼此相互推送信息;XHR受到域的限制,而WebSocket允许跨域通信。


Ajax技术很聪明的一点是没有设计要使用的方式。WebSocket为指定目标创建,用于双向推送消息。

关于web实时通信技术的发展(poll,ajax,comet等)以及websocket的介绍具体请参见:

使用 HTML5 WebSocket 构建实时 Web 应用 http://www.linuxidc.com/Linux/2012-02/54014.htm

链接的文章介绍了websocket的旧版协议草案,并用.net实现了该草案。

在2011年7月份,websocket发布了最新版的协议草案,草案的最新版本是草案10,草案的链接地址为:http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-10,新的草案增加了安全性和可扩展性。

新草案客户端与服务器端的握手协议:

客户端发起websocket请求

[javascript]
  1. socket = new MozWebSocket("ws://localhost:12345/websocket/server.php")  

这里用的是firefox浏览器,所以用MozWebSocket(),其他浏览器像chrome,需要用WebSocket()

请求头信息格式:

[plain]
  1. GET /chat HTTP/1.1  
  2. Host: localhost:12345  
  3. Upgrade: websocket  
  4. Connection: Upgrade  
  5. Sec-WebSocket-Key: kHuChwCCkr9PZDPWo+nMXg==  
  6. Sec-WebSocket-Origin: http://localhost  
  7. Sec-WebSocket-Protocol: websocket  
  8. Sec-WebSocket-Version: 8  

服务器端取得请求信息,主要是Sec-WebSocket-Key的值,取得该值之后,连接上字符串258EAFA5-E914-47DA-95CA-C5AB0DC85B11,然后计算其sha1散列值,生成一个20位的字符串,再对该字符串进行base64编码,最后得到的值,按照下列响应信息格式返回给客户端

[plain]
  1. HTTP/1.1 101 Switching Protocols  
  2. Upgrade: websocket  
  3. Connection: Upgrade  
  4. Sec-WebSocket-Accept: 0VXXdxNQxqV7u7vtIhrxqdYUgRA=  
  5. Sec-WebSocket-Protocol: websocket  

客户端接收到服务器的响应信息,连接建立。

新草案的数据传输格式请参考下文:

    2、数据传输的格式:

以下是一个格式标准图:

点击查看原图

FIN:1位,用来表明这是一个消息的最后的消息片断,当然第一个消息片断也可能是最后的一个消息片断;

RSV1, RSV2, RSV3: 分别都是1位,如果双方之间没有约定自定义协议,那么这几位的值都必须为0,否则必须断掉WebSocket连接;

Opcode:4位操作码,定义有效负载数据,如果收到了一个未知的操作码,连接也必须断掉,以下是定义的操作码:
      *  %x0 表示连续消息片断
      *  %x1 表示文本消息片断
      *  %x2 表未二进制消息片断
      *  %x3-7 为将来的非控制消息片断保留的操作码
      *  %x8 表示连接关闭
      *  %x9 表示心跳检查的ping
      *  %xA 表示心跳检查的pong
      *  %xB-F 为将来的控制消息片断的保留操作码

Mask:1位,定义传输的数据是否有加掩码,如果设置为1,掩码键必须放在masking-key区域,客户端发送给服务端的所有消息,此位的值都是1;

Payload length: 传输数据的长度,以字节的形式表示:7位、7+16位、或者7+64位。如果这个值以字节表示是0-125这个范围,那这个值就表示传输数据的长度;如果这个值是126,则随后的两个字节表示的是一个16进制无符号数,用来表示传输数据的长度;如果这个值是127,则随后的是8个字节表示的一个64位无符合数,这个数用来表示传输数据的长度。多字节长度的数量是以网络字节的顺序表示。负载数据的长度为扩展数据及应用数据之和,扩展数据的长度可能为0,因而此时负载数据的长度就为应用数据的长度。

Masking-key:0或4个字节,客户端发送给服务端的数据,都是通过内嵌的一个32位值作为掩码的;掩码键只有在掩码位设置为1的时候存在。
Payload data:  (x+y)位,负载数据为扩展数据及应用数据长度之和。
Extension data:x位,如果客户端与服务端之间没有特殊约定,那么扩展数据的长度始终为0,任何的扩展都必须指定扩展数据的长度,或者长度的计算方式,以及在握手时如何确定正确的握手方式。如果存在扩展数据,则扩展数据就会包括在负载数据的长度之内。
Application data:y位,任意的应用数据,放在扩展数据之后,应用数据的长度=负载数据的长度-扩展数据的长度。

    数据帧协议是按照扩展的巴科斯范式(ANBF:Augmented Backus-Naur Form RFC5234)组成的:       

[plain]
ws-frame                = frame-fin   
                          frame-rsv1   
                          frame-rsv2   
                          frame-rsv3   
                          frame-opcode   
                          frame-masked   
                          frame-payload-length   
                          [ frame-masking-key ]   
                          frame-payload-data   
 
 frame-fin               = %x0 ; 表示这不是当前消息的最后一帧,后面还有消息   
                        / %x1 ; 表示这是当前消息的最后一帧   
 
frame-rsv1              = %x0   
                          ; 1 bit, 如果没有扩展约定,该值必须为0    
 
 
frame-rsv2              = %x0   
                          ; 1 bit, 如果没有扩展约定,该值必须为0   
 
frame-rsv3              = %x0   
                          ; 1 bit, 如果没有扩展约定,该值必须为0   
 
frame-opcode            = %x0 ; 表示这是一个连续帧消息   
                        / %x1 ; 表示文本消息   
                        / %x2 ; 表示二进制消息   
                        / %x3-7 ; 保留   
                        / %x8 ; 表示客户端发起的关闭   
                        / %x9 ; ping(用于心跳)   
                        / %xA ; pong(用于心跳)   
                        / %xB-F ; 保留    
   
frame-masked            = %x0 ; 数据帧没有加掩码,后面没有掩码key   
                        / %x1 ; 数据帧加了掩码,后面有掩码key   
 
 
frame-payload-length    = %x00-7D   
                        / %x7E frame-payload-length-16   
                        / %x7F frame-payload-length-63   
   ; 表示数据帧的长度   

 
 
frame-payload-length-16 = %x0000-FFFF   
   ; 表示数据帧的长度   
   
frame-payload-length-63 = %x0000000000000000-7FFFFFFFFFFFFFFF   
   ; 表示数据帧的长度   
   
frame-masking-key       = 4( %0x00-FF ) ; 掩码key,只有当掩码位为1时出现   
   
frame-payload-data      = (frame-masked-extension-data   
                           frame-masked-application-data)   ; 当掩码位为1时,这里的数据为带掩码的数据,扩展数据及应用数据都带掩码   
                        / (frame-unmasked-extension-data   
                           frame-unmasked-application-data) ; 当掩码位为0时,这里的数据为不带掩码的数据,扩展数据及应用数据都不带掩码   
   
frame-masked-extension-data     = *( %x00-FF ) ; 目前保留,以后定义   
   
frame-masked-application-data   = *( %x00-FF )   
   
frame-unmasked-extension-data   = *( %x00-FF ) ; 目前保留,以后定义   
   
frame-unmasked-application-data = *( %x00-FF ) 
此条目发表在technologys分类目录,贴了标签。将固定链接加入收藏夹。