Websocket
好久没有写积累的东西了,都是零零点点的。今天花点时间整理一下关于 websocket 的东西
# websocket 是什么
webscoket 实际上是一种传输协议,它可以实现前后端的全双工协议传输。即可以实现传统的前端向后端传输,也可以由服务端主动的向前端发送。它和 http 协议没有特别多的交集,只有在建立连接的时候会使用 http 去升级协议。
# websocket 和其他的方案对比
# 轮询
轮训就是最原始的实现方案了。。。
# 长轮询
请求发出后,会被服务端 hold 住,此时一直处于 pending 状态,直到有数据返回或者超时再开启下一次。
# server send events
只可服务端发送消息。客户端利用 eventsource api,和 websocket 类似,主动监听消息
# websocket 请求协议
- 以 socket.io 为例子 (opens new window),chrome 环境
general:
Request URL: wss://socket-io-chat.now.sh/socket.io/?EIO=3&transport=websocket&sid=DrOBxwSloVfVqwNzAFZF
Request Method: GET
Status Code: 101 Switching Protocols
Response Headers
cache-control: s-maxage=0
CF-RAY: 495d0c3c9c6a9bb7-SJC
Connection: upgrade
Date: Tue, 08 Jan 2019 07:45:59 GMT
Expect-CT: max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
now: 1
Sec-WebSocket-Accept: ihMiSxjFOglAVillOgmKTw1gDeQ=
Sec-WebSocket-Extensions: permessage-deflate; client_no_context_takeover
Sec-WebSocket-Version: 13
Server: cloudflare
Upgrade: websocket
WebSocket-Server: uWebSockets
x-now-trace: sfo1
Request Headers:
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
Cache-Control: no-cache
Connection: Upgrade
Host: socket-io-chat.now.sh
Origin: https://socket-io-chat.now.sh
Pragma: no-cache
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
Sec-WebSocket-Key: L9i2lXAHNH6vxZ12RRwcAw==
Sec-WebSocket-Version: 13
Upgrade: websocket
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3663.0 Safari/537.36
Query String Parameters
EIO: 3
transport: websocket
sid: DrOBxwSloVfVqwNzAFZF
?EIO=3&transport=polling&t=MWiRCui&sid=DrOBxwSloVfVqwNzAFZF ?EIO=3&transport=websocket&sid=DrOBxwSloVfVqwNzAFZF
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
注意请求头的 connection 和 upgrade 字段,请求成功后服务端会返回 101 Switching Protocols 来切换协议
。之后的传输都是使用类似与 tcp 的包来直接进行帧传输。可以为二进制 buffer array 或者文本 text/plain 类型
- 以下数据为 chrome 解析出来的 Messages 数据展示
2probe 6 16:46:46.089
3probe 6 16:46:46.361
5 1 16:46:46.458
42["add user","yuicer"] 23 16:46:47.424
42["login",{"numUsers":18}] 27 16:46:47.626
42["new message",{"username":"robot","message":"Welcome yuicer"}] 65 16:46:47.927
42["typing"] 12 16:46:50.795
42["new message","hah"] 23 16:46:51.344
42["stop typing"] 17 16:46:51.345
42["user joined",{"username":"test","numUsers":19}] 51 16:46:57.851
42["new message",{"username":"robot","message":"Welcome test"}] 63 16:46:58.060
42["typing",{"username":"test"}] 32 16:47:00.984
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
上面是基于 engine.io (opens new window)的格式遍写的内容 初始的帧格式如下
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 ... :
+---------------------------------------------------------------+
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 大规模群消息优化
- 只对在线人群进行广播
- 消息的收发和消息的处理没有强关联
- 假设有 a ,b 用户,s 服务端,a 发送的消息,经过 ws 收发, a 会立即收到反馈并显示在 a 的 ui 上,然后此消息会经过服务端逻辑处理,如果消息确认无效或没有发送成功,会再发送给 a 一条消息,去标明发送的消息欧问题,此时 a 的 ui 上的这条消息会出现带红色感叹号的提示。同时 b 不会收到该消息。如果该消息为红包类型消息,a 也会立即收到,b 则会在服务端处理相应的逻辑之后才会收到该消息。
- 总结为,对于发送者和接受者是不同的逻辑,发送方要求实时展示,接收方要求准确无误。
- 如果消息频繁,可将实时发送更改为非实时的间隔段打包发送,即对于在 100ms 内的消息进行打包,然后 100 ms 发送一次。