Websocket草案10协议

 

websocket草案10,链接地址为:http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-10

1、握手的标准:

1)、最老的websocket草案标准中是没有安全key,草案7.5、7.6中有两个安全key,而现在的草案10中只有一个安全key,即将7.5、7.6中http头中的"Sec-WebSocket-Key1"与"Sec-WebSocket-Key2"合并为了一个"Sec-WebSocket-Key"

2)、把http头中Upgrade的值由"WebSocket"修改为了"websocket";

3)、把http头中的"Origin"修改为了"Sec-WebSocket-Origin";

4)、增加了http头"Sec-WebSocket-Accept",用来返回原来草案7.5、7.6服务器返回给客户端的握手验证,原来是以内容的形式返回,现在是放到了http头中;另外服务器返回客户端的验证方式也变了,后面会有介绍。

 

示例说明:

 

1.1.客户端发起连接请求

GET /10.15.1.218:12345/chat?key=value\r\n

HTTP/1.1\r\n
Upgrade: websocket\r\n
Connection: Upgrade\r\n
Host: 10.15.1.218:12345\r\n
Sec-WebSocket-Origin: null\r\n
Sec-WebSocket-Key: 4tAjitqO9So2Wu8lkrsq3w==\r\n
Sec-WebSocket-Version: 8\r\n\r\n

 

这是类似于HTTP的头,注意每行数据结尾结束符是"\r\n", 最后的结束符是"\r\n\r\n"。

Sec-WebSocket-Key后面的长度为24的字符串是客户端随机生成的,我们暂时叫他cli_key,服务器必须用它经过一定的运算规则生成服务器端的key,暂时叫做ser_key,然后把ser_key发回去,客户端验证正确后,握手成功!

 

1.2.制作服务器端的密钥

服务器端将cli_key(长度24)截取出来4tAjitqO9So2Wu8lkrsq3w==

用它和自定义的一个字符串(长度36):258EAFA5-E914-47DA-95CA-C5AB0DC85B11

连接起来,像这样:4tAjitqO9So2Wu8lkrsq3w==258EAFA5-E914-47DA-95CA-C5AB0DC85B11

然后把这一长串经过SHA-1算法加密,得到长度为20字节的二进制数据,再将这些数据经过Base64编码,最终得到服务端的密钥,也就是ser_key:bEVeGLZrb9fS3Rj8WzExJdCsedg=

 

1.3.服务端返回密钥

然后需要把密钥返回给客户端,完成握手,发送的数据格式如下:

HTTP/1.1 101 Switching Protocols\r\n

Upgrade: websocket\r\n

Connection: Upgrade\r\n

Sec-WebSocket-Accept: bEVeGLZrb9fS3Rj8WzExJdCsedg=\r\n\r\n

 

至此,算是握手成功了!

 

    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)组成的:    

 1    ws-frame                = frame-fin
 2                              frame-rsv1
 3                              frame-rsv2
 4                              frame-rsv3
 5                              frame-opcode
 6                              frame-masked
 7                              frame-payload-length
 8                              [ frame-masking-key ]
 9                              frame-payload-data
10 
11    frame-fin               = %x0 ; 表示这不是当前消息的最后一帧,后面还有消息
12                            / %x1 ; 表示这是当前消息的最后一帧
13 
14    frame-rsv1              = %x0
15                              ; 1 bit, 如果没有扩展约定,该值必须为0
16 
17    frame-rsv2              = %x0
18                              ; 1 bit, 如果没有扩展约定,该值必须为0
19 
20    frame-rsv3              = %x0
21                              ; 1 bit, 如果没有扩展约定,该值必须为0
22 
23    frame-opcode            = %x0 ; 表示这是一个连续帧消息
24                            / %x1 ; 表示文本消息
25                            / %x2 ; 表示二进制消息
26                            / %x3-7 ; 保留
27                            / %x8 ; 表示客户端发起的关闭
28                            / %x9 ; ping(用于心跳)
29                            / %xA ; pong(用于心跳)
30                            / %xB-F ; 保留
31 
32    frame-masked            = %x0 ; 数据帧没有加掩码,后面没有掩码key
33                            / %x1 ; 数据帧加了掩码,后面有掩码key
34 
35    frame-payload-length    = %x00-7D
36                            / %x7E frame-payload-length-16
37                            / %x7F frame-payload-length-63
38                ; 表示数据帧的长度
39 
40    frame-payload-length-16 = %x0000-FFFF
41                ; 表示数据帧的长度
42 
43    frame-payload-length-63 = %x0000000000000000-7FFFFFFFFFFFFFFF
44                ; 表示数据帧的长度
45 
46    frame-masking-key       = 4( %0x00-FF ) ; 掩码key,只有当掩码位为1时出现
47 
48    frame-payload-data      = (frame-masked-extension-data
49                               frame-masked-application-data)   ; 当掩码位为1时,这里的数据为带掩码的数据,扩展数据及应用数据都带掩码
50                            / (frame-unmasked-extension-data
51                               frame-unmasked-application-data) ; 当掩码位为0时,这里的数据为不带掩码的数据,扩展数据及应用数据都不带掩码
52 
53    frame-masked-extension-data     = *( %x00-FF ) ; 目前保留,以后定义
54 
55    frame-masked-application-data   = *( %x00-FF )
56 
57    frame-unmasked-extension-data   = *( %x00-FF ) ; 目前保留,以后定义
58 
59    frame-unmasked-application-data = *( %x00-FF )

示例说明:

协议中规定客户端发向服务器的数据必须有掩码,比如需要发送一个字符串“Hello”,

“Hello“的ascii码:

                         H        e              l            l            o

十六进制         0x48     0x65        0x6c     0x6c     0x6f

十进制             72        101          108      108       111

 但是实际发出的数据是这样的:

 -------head-----掩码0---1------2-----3-----H----e-----l------l------o----

   0x81 0x85      0x37  0xfa  0x21  0x3d    0x7f   0x9f  0x4d  0x51  0x58

head头部我不在这里说了,看下协议很容易理解,head后是4字节的掩码,随机生成的,再后面是数据了,你可能发现数据变了,这些数据是 hello的ascii码和掩码做异或运算算出来的。

运算规则是这样的, 第零个字符'H'和第零个掩码异或, 第一个字符'e'和第一个掩码异或......

其实,用c语言表示就是res = str[n]^mask[n%4]

  

      服务器发出数据可以有掩码, 也可以没有掩码。

发出一个“Hello”字符串可以发出和客户端一样的数据,也可以发出像下面的无掩码的:

                  0x81 0x05 0x48 0x65 0x6c 0x6c 0x6f
                                     H       e       l        l       o

这就是头部信息加上原始数据啦。

 

posted @ 2012-12-21 17:47  小壁虎借尾巴  阅读(700)  评论(0编辑  收藏  举报