websocket协议标准和实现

leeir 2016 10.13

协议实现流程

  • 握手过程
  • 解开数据帧
  • 打包数据帧回给客户端

client

server

ws://    Sec-WebSocket-Key

Sec-WebSocket-Accept:base64(sha1(Sec-WebSocket-Key+MASK))

            基于tcp的握手流程

1、服务端获取到"Sec-WebSocket-Key"
2、将这个key与字符串'258EAFA5-E914-47DA-95CA-C5AB0DC85B11' concat
3、对新的字符串通过sha1散列算法进行计算
4、base64编码
5、写入"Sec-WebSocket-Accept"响应给客户端
6、完成握手

握手demo

协议标准:解包数据帧

0                   1                   2                   3
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
+-+-+-+-+-------+-+-------------+-------------------------------+
|F|R|R|R| opcode|M| Payload len |    Extended payload length    |
|I|S|S|S|  (4)  |A|     (7)     |             (16/64)           |
|N|V|V|V|       |S|             |   (if payload len==126/127)   |
| |1|2|3|       |K|             |                               |
+-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
|     Extended payload length continued, if payload len == 127  |
+ - - - - - - - - - - - - - - - +-------------------------------+
|                               |Masking-key, if MASK set to 1  |
+-------------------------------+-------------------------------+
| Masking-key (continued)       |          Payload Data         |
+-------------------------------- - - - - - - - - - - - - - - - +
:                     Payload Data continued ...                :
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
|                     Payload Data continued ...                |
+---------------------------------------------------------------+
上图引自RFC6455 https://tools.ietf.org/html/rfc6455 细节可参考官方文档

先分析下数据帧的格式规范吧
(1字节为8个bit)
第一个字节:
FIN      1bit 表示信息的最后一帧,flag,也就是标记符
RSV 1-3  1bit 官方说是备用的默认现在都是0
Opcode   4bit 帧类型
第二个字节:
Mask     1bit 掩码,是否加密数据,默认必须置为1 (这里很蛋疼)
PayloadLength  7bit 数据的长度 7个bit位存储无符号整型的话那么就是0-127这是存储不了大的数据的
所以paload是可扩展的长度 规则为Payload length:  125以内的7 bits, 7+16 bits, or 7+64 bits
下面4个bit是maskKey但是是建立在mask为1的情况下的如果MASK!=1则没有maskKEY的掩码实体
Masking-key      1 or 4 bit 掩码
在下面全部是payload的数据部分
Payload data

协议标准:打包数据帧

 * opcode的对照表
 * 参考 https://tools.ietf.org/html/rfc6455 11.7部分
 |Opcode  | Meaning                             | Reference |
-+--------+-------------------------------------+-----------|
 | 0      | Continuation Frame                  | RFC 6455  |
-+--------+-------------------------------------+-----------|
 | 1      | Text Frame                          | RFC 6455  |
-+--------+-------------------------------------+-----------|
 | 2      | Binary Frame                        | RFC 6455  |
-+--------+-------------------------------------+-----------|
 | 8      | Connection Close Frame              | RFC 6455  |
-+--------+-------------------------------------+-----------|
 | 9      | Ping Frame                          | RFC 6455  |
-+--------+-------------------------------------+-----------|
 | 10     | Pong Frame                          | RFC 6455  |
-+--------+-------------------------------------+-----------|
0为多个frame 做分片时用到
1为字符串数据帧
2为二进制数据帧
8控制型 连接关闭
9和10为心跳检测控制 发送ping  回复pong 具有相互性 即服务端对客户端可以做心跳检测 客户端也可以对服务端做心跳检测

frame结构:
  FIN: 1,//结束标识符 分片时候会体现 当为0时代表后面还有数据 1代表结束
  Opcode: 1, //对应关系见上表
  Mask: 1,   //是否需要掩码加密
  PayloadLength: 5,//payload部分数据长度
  MaskingKey: [ 39, 158, 30, 16 ],//掩码实体
  PayloadData: '哈哈哈 我只是一个payload'//payload数据实体

分片

  编号:      0  1  ....  n-2 n-1
  分片:     |——|——|......|——|——|
  FIN:      0  0  ....   0  1
  Opcode:   !0 0  ....   0  0

将原本一个大的帧拆分成数个小的帧

>>NOTE:
1、多个数据包但是前面几个数据帧的fin都为0最后一个为1来结束分片标识 每一帧的Opcode为0
2、消息的分片必须由发送者按给定的顺序发送给接收者
3、控制帧禁止分片
4、接受者不必按顺序缓存整个frame来处理

参考文档

nodejs(buffer,net,crypto)

RFC6455

nodejs-websocket

http://jinnianshilongnian.iteye.com/blog/1913795    中文版RFC6455
https://tools.ietf.org/html/rfc6455                 官方文档

QA

Made with Slides.com