WebSocket说明(自己整理)

WebSocket说明
一、简介

WebSocket是一种在单个TCP连接上进行全双工通信的协议。
以下从连接流程,数据传递,保持连接+心跳,数据帧格式(包括掩码算法)几个方面介绍WebSocket,重点是连接建立过程,和数据交换过程。

二、应用场景
没有WebSocket的业务场景时, 客户端想知道服务端的处理进度,就要使用 Ajax 进行轮询,让浏览器隔几秒就向服务器发一次请求,这对服务器压力较高。另外一种轮询就是采用 long poll 的方式,这就跟打电话差不多,没收到消息就一直不挂电话,也就是说,客户端发起连接后,如果没消息,就一直不返回 Response 给客户端,连接阶段一直是阻塞的。
WebSocket 解决了 HTTP 的这几个难题。首先,当服务器完成协议升级后( HTTP -> WebSocket ),服务端可以主动推送信息给客户端,解决了轮询造成的同步延迟问题。由于 WebSocket 只需要一次 HTTP 握手,服务端就能一直与客户端保持通讯,直到关闭连接,这样就解决了服务器需要反复解析 HTTP 协议,减少了资源的开销。

三、WebSocket帧格式

WebSocket客户端、服务端通信的最小单位是帧(frame),由1个或多个帧组成一条完整的消息(message)。
(1)发送端:将消息切割成多个帧,并发送给服务端;
(2)接收端:接收消息帧,并将关联的帧重新组装成完整的消息;

下发帧标准格式
0 1 2 3
0 -> 31
FIN(0)
RSV1, RSV2, RSV3(1:3)
opcode(4:7) Mask(8)
PayLoadLength(9:15) Extended PayLoadLength(16:64)
(if payLoadLength == 126/127)
Extended payLoadLength continued, if payLoadLength == 127
Masking-key, if Mask set to 1
Masking-key (continued) PayLoadData
PayLoadData continued …
1.数据帧格式概览

2.数据帧格式详解

(1)FIN:1个比特。如果是1 ,表示这是消息的最后一个分片,反之则不是。
(2)RSV1, RSV2, RSV3:各占1个比特。
一般情况下全为0。当客户端、服务端协商采用WebSocket扩展时,这三个标志位可以非0,且值的含义由扩展进行定义。如果出现非零的值,且并没有采用WebSocket扩展,连接出错。
(3)Opcode: 4个比特。
Opcode的值决定了应该如何解析后续的数据载荷(data payload)。如果操作代码是不认识的,那么接收端应该断开连接(fail the connection)。可选的操作代码如下:
1)%x0:表示一个延续帧。当Opcode为0时,表示本次数据传输采用了数据分片,当前收到的数据帧为其中一个数据分片。
2)%x1:表示这是一个文本帧(frame)
3)%x2:表示这是一个二进制帧(frame)
4)%x3-7:保留的操作代码,用于后续定义的非控制帧。
5)%x8:表示连接断开。
6)%x9:表示这是一个ping操作。
7)%xA:表示这是一个pong操作。
8)%xB-F:保留的操作代码,用于后续定义的控制帧。

(4)Mask: 1个比特。
表示是否要对数据载荷进行掩码操作。从客户端向服务端发送数据时,需要对数据进行掩码操作;从服务端向客户端发送数据时,不需要对数据进行掩码操作。
如果服务端接收到的数据没有进行过掩码操作,服务端需要断开连接。
如果Mask是1,那么在Masking-key中会定义一个掩码键(masking key),并用这个掩码键来对数据载荷进行反掩码。所有客户端发送到服务端的数据帧,Mask都是1。

(5)Payload length:数据载荷的长度,单位是字节。为7位,或7+16位,或1+64位。
假设数Payload length === x,如果
1)x为0~126:数据的长度为x字节。
2)x为126:后续2个字节代表一个16位的无符号整数,该无符号整数的值为数据的长度。
3)x为127:后续8个字节代表一个64位的无符号整数(最高位为0),该无符号整数的值为数据的长度。
4)此外,如果payload length占用了多个字节的话,payload length的二进制表达采用网络序(big endian,重要的位在前)。

(6)Masking-key:0或4字节(32位)
所有从客户端传送到服务端的数据帧,数据载荷都进行了掩码操作,Mask为1,且携带了4字节的Masking-key。如果Mask为0,则没有Masking-key。
备注:载荷数据的长度,不包括mask key的长度。
(7)Payload data:(x+y) 字节
(8)载荷数据:包括了扩展数据、应用数据。其中,扩展数据x字节,应用数据y字节。
1)扩展数据:如果没有协商使用扩展的话,扩展数据数据为0字节。所有的扩展都必须声明扩展数据的长度,或者可以如何计算出扩展数据的长度。此外,扩展如何使用必须在握手阶段就协商好。如果扩展数据存在,那么载荷数据长度必须将扩展数据的长度包含在内。
2)应用数据:任意的应用数据,在扩展数据之后(如果存在扩展数据),占据了数据帧剩余的位置。载荷数据长度 减去 扩展数据长度,就得到应用数据的长度。

3.掩码算法

掩码键(Masking-key)是由客户端挑选出来的32位的随机数。掩码操作不会影响数据载荷的长度。掩码、反掩码操作都采用如下算法:
首先,假设:
original-octet-i:为原始数据的第i字节。
transformed-octet-i:为转换后的数据的第i字节。
j:为i mod 4的结果。
masking-key-octet-j:为mask key第j字节。
算法描述为: original-octet-i 与 masking-key-octet-j 异或后,得到 transformed-octet-i。
j = i MOD 4
transformed-octet-i = original-octet-i XOR masking-key-octet-j

四、WebSocket流程
4.1 连接流程
WebSocket复用了HTTP的握手通道。具体指的是,客户端通过HTTP请求与WebSocket服务端协商升级协议。协议升级完成后,后续的数据交换则遵照WebSocket的协议。
流程如下
1.客户端申请upgrade

#采用的是标准的HTTP报文格式,且只支持GET方法。

GET / HTTP/1.1
Host: localhost:8080
Origin: http://127.0.0.1:3000
Connection: Upgrade
Upgrade:websocket
Sec-WebSocket-Version: 13
Sec-WebSocket-Key: w4v7O6xFTi36lq3RNcgctw==
2.服务端响应协议升级

状态代码101表示协议切换。到此完成协议升级,后续的数据交互都按照新的协议来。

HTTP/1.1 101 Switching Protocols
Connection:Upgrade
Upgrade: websocket
Sec-WebSocket-Accept: Oy4NRAQ13jhfONC7bP8dTKb4PTU=

3.Sec-WebSocket-Accept的计算

Sec-WebSocket-Accept根据客户端请求首部的Sec-WebSocket-Key计算出来。
计算公式为:
(1). 将Sec-WebSocket-Key跟258EAFA5-E914-47DA-95CA-C5AB0DC85B11拼接。
(2). 通过SHA1计算出摘要,并转成base64字符串。

4.2 数据传递

一旦WebSocket客户端、服务端建立连接后,后续的操作都是基于数据帧的传递。
WebSocket根据opcode来区分操作的类型。比如0x8表示断开连接,0x0-0x2表示数据交互。

1.数据分片
WebSocket的每条消息可能被切分成多个数据帧。当WebSocket的接收方收到一个数据帧时,会根据FIN的值来判断,是否已经收到消息的最后一个数据帧。
FIN=1表示当前数据帧为消息的最后一个数据帧,此时接收方已经收到完整的消息,可以对消息进行处理。FIN=0,则接收方还需要继续监听接收其余的数据帧。
此外,opcode在数据交换的场景下,表示的是数据的类型。0x01表示文本,0x02表示二进制。而0x00比较特殊,表示延续帧(continuation frame),就是完整消息对应的数据帧还没接收完。

2.数据分片例子
第一条消息
FIN=1, 表示是当前消息的最后一个数据帧。服务端收到当前数据帧后,可以处理消息。opcode=0x1,表示客户端发送的是文本类型。
第二条消息
FIN=0,opcode=0x1,表示发送的是文本类型,且消息还没发送完成,还有后续的数据帧。
FIN=0,opcode=0x0,表示消息还没发送完成,还有后续的数据帧,当前的数据帧需要接在上一条数据帧之后。
FIN=1,opcode=0x0,表示消息已经发送完成,没有后续的数据帧,当前的数据帧需要接在上一条数据帧之后。服务端可以将关联的数据帧组装成完整的消息。
Client: FIN=1, opcode=0x1, msg=“hello”
Server: (process complete message immediately) Hi.
Client: FIN=0, opcode=0x1, msg=“and a”
Server: (listening, new message containing text started)
Client: FIN=0, opcode=0x0, msg=“happy new”
Server: (listening, payload concatenated to previous message)
Client: FIN=1, opcode=0x0, msg=“year!”
Server: (process complete message) Happy new year to you too!
4.3连接保持+心跳

WebSocket为了保持客户端、服务端的实时双向通信,需要确保客户端、服务端之间的TCP通道保持连接没有断开。然而,对于长时间没有数据往来的连接,如果依旧长时间保持着,可能会浪费包括的连接资源。
但不排除有些场景,客户端、服务端虽然长时间没有数据往来,但仍需要保持连接。这个时候,可以采用心跳来实现。

(1)发送方->接收方:ping
(2)接收方->发送方:pong
ping、pong的操作,对应的是WebSocket的两个控制帧,opcode分别是0x9、0xA。目前的话,浏览器中没有相关api发送ping给服务器,只能由服务器发ping给浏览器,浏览器返回pong消息;

4.4 断开连接
Opcode为0x8则表示断开连接

各种业务的具体帧

o A single-frame unmasked text message

  *  0x81 0x05 0x48 0x65 0x6c 0x6c 0x6f (contains "Hello")

0x81 : 是尾帧,type是文本
0x05 : 不带掩码,长度是5(最大125),126,127,时不再表示长度而是表示长度的类型
0x48 0x65 0x6c 0x6c 0x6f : “Hello”的ascii码

o A single-frame masked text message

  *  0x81 0x85 0x37 0xfa 0x21 0x3d 0x7f 0x9f 0x4d 0x51 0x58
     (contains "Hello")

0x81 : 是尾帧,type是文本
0x85 : 带掩码,长度是5
0x37 0xfa 0x21 0x3d :掩码
0x7f 0x9f 0x4d 0x51 0x58 : 算法如下,拿’H’举例
‘H’ = 0x48  i = 0 (‘H’序号 ), j = i % 4 = 0 , maskkey[j] = 0x37 , 0x48 XOR 0x37 = 0x7f

o A fragmented unmasked text message

  *  0x01 0x03 0x48 0x65 0x6c (contains "Hel")

0x01 : 不是尾帧,type是文本
0x03 : 不带掩码,长度是3
0x48 0x65 : “Hel”

  *  0x80 0x02 0x6c 0x6f (contains "lo")

0x80 : 是尾帧,type是延续
0x02 : 不带掩码,长度是2
0x6c 0x6f : “l0”

o Unmasked Ping request and masked Ping response

  *  0x89 0x05 0x48 0x65 0x6c 0x6c 0x6f (contains a body of "Hello",
     but the contents of the body are arbitrary)

0x89 : 是尾帧,type是ping
0x05 : 不带掩码,长度是5
0x48 0x65 0x6c 0x6c 0x6f : 文本

  *  0x8a 0x85 0x37 0xfa 0x21 0x3d 0x7f 0x9f 0x4d 0x51 0x58
     (contains a body of "Hello", matching the body of the ping)

0x8a : 是尾帧,type是pong
0x85 : 带掩码,长度是5
0x37 0xfa 0x21 0x3d : 掩码
0x7f 0x9f 0x4d 0x51 0x58 : 加密后的文本

o 256 bytes binary message in a single unmasked frame

  *  0x82 0x7E 0x0100 [256 bytes of binary data]

0x82 : 是尾帧,type是二进制
0x7E : 不带掩码,长度类型是126
0x0100 : 长度是256
[...] : 数据

o 64KiB binary message in a single unmasked frame

  *  0x82 0x7F 0x0000000000010000 [65536 bytes of binary data]

0x82 : 是尾帧,type是二进制
0x7F : 不带掩码,长度类型是127
0x0000000000010000 : 长度是65536	
[...] : 数据
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值