- WebSocket解决了什么问题?
- 简介
- 设计原理
- 协议格式
- 数据帧格式
- 掩码
- 掩码算法
- 掩码作用
- 流程
- URI格式
- 握手建立
- 客户端
- 服务端
- 如何验证握手被服务器接受?
- 心跳帧
- 关闭帧
- 关闭帧的错误码
- 如何维持长连接
- 应用场景
- 负载均衡
- Chrome分析WebSocket
- 过滤器
- 表格列
- 消息颜色
- 参考资料
推荐一个免费的架构师课程 C/C++Linux服务器开发/后台架构师 WebSocket解决了什么问题?
我们已经有了 HTTP 协议,为什么还需要另一个协议?它能带来什么好处?
因为 HTTP 协议有一个缺陷:通信只能由客户端发起。所以我们如果想要获取
举例来说,我们想了解今天的天气,只能是客户端向服务器发出请求,服务器返回查询结果。HTTP 协议做不到服务器主动向客户端推送信息,所以服务器有连续的状态变化,客户端要获知就非常麻烦,我们只能使用轮询的方式去查询。轮询的效率非常低,非常浪费资源,所以websocket就诞生了。
WebSocket在2008年诞生,2011成为国际标准,如今所有浏览器均已支持,Websocket已经广泛应用。其定义在RFC6455
WebSocket的最大特点就是服务器可以向客户端推送消息,是真正的双向平等对话,属于服务器推送技术。其特点如下:
- 建立在TCP协议之上,服务端实现比较容易。
- 与HTTP协议有着良好的兼容性。默认端口也是80和443,并且握手采取HTTP协议。
- 数据格式轻量,性能开销小,通信高效。
- 可以发送文本也可以发送二进制数据。
- 没有同源限制,客户端可以与任意服务器通信。
- 协议标识符是WS,加密则为wss,服务器网址就是URL。
![image.png](https://img-blog.csdnimg.cn/img_convert/44d2bfeb76484e56308f49bbfb22144f.png#clientId=ub1df7dfe-87ca-4&from=paste&height=746&id=ua3b07b68&margin=[object Object]&name=image.png&originHeight=746&originWidth=929&originalType=binary&ratio=1&size=223239&status=done&style=none&taskId=u585e28c1-4956-44d3-9ef1-30b6b0b6d83&width=929)
设计原理- 元数据:与HTTP协议不同,websocket的元数据是由应用层自行传输的。
- websocket是基于帧来传输而不是基于流的,每一帧要么承载字符数据,要么成长二进制数据。
- 基于浏览器同源策略模型,可以使用Access-Control-Allow-Origin等头部。
- 基于URI、子协议支持同主机同端口上的多个服务。
学习协议必然要学习他的协议定义,WebSocket的最小单位是帧,由1个或多个帧组成一条消息。
- 发送端:将消息切割成多个帧,并发送。
- 接收端:接收消息帧,并将关联的帧重新组装成完整的消息。
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 ... | +---------------------------------------------------------------+
- FIN:1bit,如果是1,表示这是消息的最后一个分片,如果是0表示不是消息的分片
- RSV1,RSV2,RSV3:各占1bit,一般情况全为0。当采用websocket扩展时,这三个标志位可以非0,且值含义由扩展进行定义。如果出现非零的值且没有采用websocket扩展连接出错。
- Opcode:4bit。
- 0:延续帧,表示本次分片,当前收到的数据帧为其中一个数据分片。
- 1:文本真。
- 2:二进制帧。
- 3-7:为非控制帧保留。
- 8:连接断开。
- 9:心跳帧ping。
- 10:心跳帧pong。
- B-F:保留
- Mask:1bit。表示是否对数据载荷进行掩码 *** 作。从客户端向服务器发送数据时需要对数据进行掩码 *** 作,当从服务端向客户端发送数据时,不需要对数据及逆行掩码 *** 作。如果服务端接收到的数据没有进行过掩码 *** 作,服务端就需要断开连接。如果mask为1,那么masking-key中会定义一个掩码键,并用这个掩码键来对数据进行反掩码。所有客户端发送到服务端的数据帧mask都是1。
- Payload Length:数据的长度,单位是字节,为7位或7+16位或1+64位。
- 0-126:数据长度为x字节。
- 126:后续2个字节为16位无符号整数,为数据的长度.
- 127:后续8个字节为64位无符号整数,为数据长度。
掩码键(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
![image.png](https://img-blog.csdnimg.cn/img_convert/8260c77e25b2eaeaf805520714e27810.png#clientId=u9ee83695-c277-4&from=paste&height=392&id=ua3df7cc7&margin=[object Object]&name=image.png&originHeight=784&originWidth=1135&originalType=binary&ratio=1&size=188314&status=done&style=none&taskId=u07f242c4-b8b5-47a2-9f5d-96f32d8b50e&width=567.5)
流程 URI格式ws-URI:ws://host:[:port]path[?query]
- 默认为80
wss-URI:wss://host:[:port]path[?query]
- 默认为443
连接说明:
- host和port:主机名与端口
- shema:是否基于SSL
- 访问资源:URI
- 握手随机数:Sec-WebSocket-Key
- 选择子协议:Sec-WebSocket-Protocol
- 扩展协议:Sec-WebSocket-Extensions
- CORS跨域:Origin
GET /?encoding=text HTTP/1.1 Host: websocket.taohui.tech Accept-Encoding: gzip, deflate Sec-WebSocket-Version: 13 Origin: http://www.websocket.org Sec-WebSocket-Extensions: permessage-deflate Sec-WebSocket-Key: c3SkgVxVCDhVCp69PJFf3A== Connection: keep-alive, Upgrade Pragma: no-cache Cache-Control: no-cache Upgrade: websocket服务端
HTTP/1.1 101 Web Socket Protocol Handshake Server: openresty/1.13.6.2 Date: Mon, 10 Dec 2018 08:14:29 GMT Connection: upgrade Access-Control-Allow-Credentials: true Access-Control-Allow-Headers: content-type Access-Control-Allow-Headers: authorization Access-Control-Allow-Headers: x-websocket-extensions Access-Control-Allow-Headers: x-websocket-version Access-Control-Allow-Headers: x-websocket-protocol Access-Control-Allow-Origin: http://www.websocket.org Sec-WebSocket-Accept: yA9O5xGLp8SbwCV//OepMPw7pEI= Upgrade: websocket如何验证握手被服务器接受?
- 请求中的 Sec-WebSocket-Key 随机数
- 例如 Sec-WebSocket-Key: A1EEou7Nnq6+BBZoAZqWlg==
- 响应中的 Sec-WebSocket-Accept 证明值
- 拼接值,服务端会将客户端的Sec-WebSocket-Key和GUID拼接,GUID在RFC4122中规定,例如A1EEou7Nnq6+BBZoAZqWlg==258EAFA5-E914-47DA-95CA-C5AB0DC85B11
- 将拼接后的进行SHA1编码
- 将SHA1编码再进行Hex base64编码
- 然后将其放到Sec-WebSocket-Accept: cT8V7OIhhhL8rbFZgoGjU4DReQ8=
websocket使用ping、pong心跳帧来维持连接
ping帧:opcode=9可以含有数据
pong帧:opcode=A必须与ping帧数据相同
websocket定义了关闭帧,opcode=8,发送关闭帧后,不再发送任何数据,接受到关闭帧后,不再接受任何数据。
关闭帧的错误码- HTTP长连接只能基于简单的超时(常见为65秒)。
- WebSocket连接基于ping/pong心跳机制维持。
- 聊天d幕
- 实时提醒
- 客户端同步
- 实时数据展示
- 远程交互
- 扫码返回
- 位置实时跟踪
- 在线状态
Websocket负载设计通常需要加入一层消息分发系统来完成,所以其负载均衡相对HTTP来说会比较复杂。
![image.png](https://img-blog.csdnimg.cn/img_convert/b7530431a90fef018327c8ff0c3d3608.png#clientId=u4229072c-da3a-4&from=paste&height=668&id=u9d6c5688&margin=[object Object]&name=image.png&originHeight=668&originWidth=555&originalType=binary&ratio=1&size=59652&status=done&style=none&taskId=u656f5c4b-1145-4624-bfa1-97b3fb4baff&width=555)
- 按类型:WS
- 属性过滤:is:running
- Data:消息负载。如果消息为纯文本,则在此处显示。对于二进制 *** 作码,此列将显示 *** 作码的名称和代码。支持以下 *** 作码:Continuationframe、Binaryframe、Connection Close frame、Ping frame和Pong frame。
- Length:消息负载的长度(以字节为单位)。
- Time:收到或发送的时间。
Chrome根据消息不同外框的颜色不同,分别如下:
- 浅绿色:发送到服务器的文本消息。
- 白色:接收到的文本消息。
- 浅黄色:WebSocket *** 作码。
- 浅红色:错误。
![image.png](https://img-blog.csdnimg.cn/img_convert/9b5d4d62cf6f8bce80a105955e55df65.png#clientId=u4229072c-da3a-4&from=paste&height=478&id=u85e70a0f&margin=[object Object]&name=image.png&originHeight=478&originWidth=873&originalType=binary&ratio=1&size=33743&status=done&style=none&taskId=u9682c5ef-c0d4-48bb-a669-69c06f5044f&width=873)
参考资料https://www.ruanyifeng.com/blog/2017/05/websocket.html
https://www.cnblogs.com/chyingp/p/websocket-deep-in.html
https://developer.mozilla.org/zh-CN/docs/Web/API/WebSocket/WebSocket
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)