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

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

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

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

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

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

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

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

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

nginx 支持websocket 配置

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

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

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

https://www.tutorialspoint.com/websockets/websockets_send_receive_messages.htm

http://www.ruanyifeng.com/blog/2017/05/websocket.html