websocket协议详解及报文分析

websocket 协议详解及报文分析

前言

相较于HTTP协议,HTTP协议有一个的缺陷为:通信只能由客户端发起。在一些场景下,这种单向请求的特点,注定了如果服务器有连续的状态变化,客户端要获知就非常麻烦。我们只能使用轮询:每隔一段时候,就发出一个询问,了解服务器有没有新的信息。最典型的场景就是聊天室。

轮询的效率低,非常浪费资源(因为必须不停连接,或者 HTTP 连接始终打开)。因此,工程师们一直在思考,有没有更好的方法。WebSocket 就是这样发明的。

websocket 协议概述

Webscoket是Web浏览器和服务器之间的一种全双工通信协议,其中WebSocket协议由IETF定为标准,WebSocket API由W3C定为标准。一旦Web客户端与服务器建立起连接,之后的全部数据通信都通过这个连接进行。通信过程中,可互相发送JSON、XML、HTML或图片等任意格式的数据。 WS(WebSocket)与HTTP协议相比,

相同点主要有:

  • 都是基于TCP的应用层协议;- 都使用Request/Response模型进行连接的建立;- 在连接的建立过程中对错误的处理方式相同,在这个阶段WS可能返回和HTTP相同的返回码;- 都可以在网络中传输数据。
    不同之处在于:
  • WS使用HTTP来建立连接,但是定义了一系列新的header域,这些域在HTTP中并不会使用;- WS的连接不能通过中间人来转发,它必须是一个直接连接;- WS连接建立之后,通信双方都可以在任何时刻向另一方发送数据;- WS连接建立之后,数据的传输使用帧来传递,不再需要Request消息;- WS的数据帧有序。

websocket 通信过程及对应报文分析

WS整个通信过程如下图所示: websocket协议详解及报文分析 - 图1 websocket协议详解及报文分析 - 图2

websocket是基于TCP的一个应用协议,与HTTP协议的关联之处在于websocket的握手数据被HTTP服务器当作HTTP包来处理,主要通过Update request HTTP包建立起连接,之后的通信全部使用websocket自己的协议。

请求:TCP连接建立后,客户端发送websocket的握手请求,请求报文头部如下:

  1. GET /uin=xxxxxxxx&app=xxxxxxxxx&token=XXXXXXXXXXXX HTTP/1.1
  2. Host: server.example.cn:443
  3. Connection: Upgrade
  4. Pragma: no-cache
  5. Cache-Control: no-cache
  6. User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36
  7. Upgrade: websocket
  8. Sec-WebSocket-Version: 13
  9. Accept-Encoding: gzip, deflate
  10. Accept-Language: zh-CN,zh;q=0.9
  11. Cookie: user_id=XXXXX
  12. Sec-WebSocket-Key: 1/2hTi/+eNURiekpNI4k5Q==
  13. Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
  14. Sec-WebSocket-Protocol: binary, base64
  • 第一行为为请求的方法,类型必须为GET,协议版本号必须大于1.1- Upgrade字段必须包含,值为websocket- Connection字段必须包含,值为Upgrade- Sec-WebSocket-Key字段必须包含 ,记录着握手过程中必不可少的键值。- Sec-WebSocket-Protocol字段必须包含 ,记录着使用的子协议- Origin(请求头):Origin用来指明请求的来源,Origin头部主要用于保护Websocket服务器免受非授权的跨域脚本调用Websocket API的请求。也就是不想没被授权的跨域访问与服务器建立连接,服务器可以通过这个字段来判断来源的域并有选择的拒绝。
    websocket协议详解及报文分析 - 图3

响应:服务器接收到请求后,返回状态码为101 Switching Protocols 的响应。

  1. HTTP/1.1 101 Switching Protocols
  2. Server: WebSockify Python/2.6.6
  3. Date: Wed, 27 May 2020 03:03:21 GMT
  4. Upgrade: websocket
  5. Connection: Upgrade
  6. Sec-WebSocket-Accept: hXXXXXXXXXXXXXXxGmM=
  7. Sec-WebSocket-Protocol: binary

websocket协议详解及报文分析 - 图4

Sec-WebSocket-Accept字段是由握手请求中的Sec-WebSocket-Key字段生层的。

握手成功后,通信不再使用HTTP协议,而采用WebSocket独立的数据帧。如下图所示,为协议帧格式:

websocket协议详解及报文分析 - 图5

  1. FIN,指明Frame是否是一个Message里最后Frame(之前说过一个Message可能又多个Frame组成);1bit,是否为信息的最后一帧
  2. RSV1-3,默认是0 (必须是0),除非有扩展定义了非零值的意义。
  3. Opcode,这个比较重要,有如下取值是被协议定义的
  4. 0x00 denotes a continuation frame
  5. 0x01 表示一个text frame
  6. 0x02 表示一个binary frame
  7. 0x03 ~~ 0x07 are reserved for further non-control frames,为将来的非控制消息片段保留测操作码
  8. 0x08 表示连接关闭
  9. 0x09 表示 ping (心跳检测相关)
  10. 0x0a 表示 pong (心跳检测相关)
  11. 0x0b ~~ 0x0f are reserved for further control frames,为将来的控制消息片段保留的操作码
  12. Mask,这个是指明“payload data”是否被计算掩码。这个和后面的Masking-key有关,如果设置为1,掩码键必须放在masking-key区域,客户端发送给服务端的所有消息,此位的值都是1
  13. Payload len,数据的长度,
  14. Masking-key0或者4bit,只有当MASK设置为1时才有效。,给一个Websocket中掩码的意义
  15. Payload data,帧真正要发送的数据,可以是任意长度,但尽管理论上帧的大小没有限制,但发送的数据不能太大,否则会导致无法高效利用网络带宽,正如上面所说Websocket提供分片。
  16. Extension data:扩展数据,如果客户端和服务端没有特殊的约定,那么扩展数据长度始终为0
  17. Application data:应用数据,

websocket 报文细节,这里由于client 和 server 端的 ip 都是127.0.0.1 :

websocket协议详解及报文分析 - 图6

nginx 支持websocket 配置

由于http 请求 涉及 反向代理 所以就涉及 nginx 配置需要支持 websocket 需要做一些特殊的配置;

  1. # 配置Nginx支持webSocket开始
  2. proxy_set_header Host $http_host;
  3. proxy_http_version 1.1;
  4. proxy_set_header Upgrade $http_upgrade;
  5. proxy_set_header Connection "upgrade";

websocket协议详解及报文分析 - 图7

其他通过代码模拟websocket 的代码可以查阅其他博客内容,这里就不赘述;

https://www.tutorialspoint.com/websockets/websockets_send_receive_messages.htm
http://www.ruanyifeng.com/blog/2017/05/websocket.html