参考资料:
一文吃透WebSocket原理
什么是 WebSocket
- 单tcp 全双工
- 服务端主动向客户端推送
- 计算机网络应用层协议,弥补http协议在持久通信能力的不足
- 服务器推送技术
- TCP上
- 与HTTP兼容,默认端口 80 和 443
- 握手阶段用HTTP协议
- 数据格式轻量
- 可以发送文本/二进制数据
- 协议标识ws,如果加密,则是wss
为什么需要 websocket
HTTP的缺陷:通信只能由客户端发起,不具备服务器推送能力。如果服务器有连续的状态变化,客户端只能轮询,效率低且浪费资源
服务端被迫维持来自每个客户端的大量不同连接
大量轮询请求会造成高开销,比如带上多余的header,造成无用的数据传输
WebSocket和HTTP的区别

图源:见参考文章
WebSocket建立握手时,是通过HTTP建立,但是建立后传输时不需要HTTP。

- Websocket 是双向通信协议,可以双向发送或接受信息,HTTP是单向的;
- HTTP/2 也可以主动推送,但是推送的是静态资源,不能推送指定信息。
WebSocket 协议原理
一个 WebSocket 握手的例子
js
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13
Origin: http://example.com
- Upgrade: 升级头,告诉服务器我想用的是 websocket 协议
- Connection: 连接头,配合upgrade,告诉服务器本次连接需要升级
- Sec-WebSocket-Key: WebSocket专用,客户端随机生成的16字节随机数,经过 Base64 编码后的字符串。避免服务端把普通HTTP请求误判为WebSocket请求,并且起到握手校验的作用,服务端会将这个key拼接上固定字符串,做哈希后再进行Base64编码,生成 Sec-WebSocket-Accept 响应头返回给客户端,客户端校验后才算握手成功。
- Sec-WebSocket-Protocol: WebSocket子协议头,客户端声明"我支持的 WebSocket 子协议",多个子协议用逗号分开。
- 作用是协商应用层协议,比如聊天系统可能定义 chat 子协议,文件传输定义 file 子协议,服务端可以选择其中一个或拒绝。
- 子协议是业务层约定,不是 websocket核心协议,可以省略。
- 是用户定义的字符串,用来区分同一个URL下,不同服务所需要的协议。
- Sec-WebSocket-Version: WebSocket 版本头,含义是客户端支持的 WebSocket 版本,服务端只支持特定版本,如果客户端版本不匹配,服务端会返回426 Upgrade Required错误,并告知支持的版本。
服务端响应
js
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=
Sec-WebSocket-Protocol: chat
- Upgrade: 告诉客户端即将升级的是 websocket 协议
- Connection: 配合Upgrade
- Sec-WebSocket-Accept: 经过服务器确认,加密过后的 Sec-WebSocket-Key。
- Sec-WebSocket-Protocol: 表示最终使用的协议
WebSocket 建立连接的过程
客户端发起HTTP请求,经过3次握手后,建立起TCP连接,HTTP请求中放WebSocket 支持的版本号等信息;
服务端收到客户端的HTTP请求后,采用HTTP协议返回数据;
客户端收到连接成功的消息后,开始借助于TCP传输信道进行全双工传输。
应用场景
- 即时聊天通信
- 多玩家游戏
- 在线协同编辑
- 实时数据流的拉取与推送
- 体育/游戏实况
- 实时地图位置
- 即时web应用程序 即时Web应用程序使用一个web套接字在客户端显示数据,数据由后端服务器连续发送,在websocket中数据被连续推送、传输到已打开的同一个连接中。
- 聊天应用程序:聊天应用程序仅使用websocket建立一次连接,能在订阅户之间交换、发布和广播消息,重复使用相同的websocket连接,用于发送和接收消息以及一对一的消息传输。
不适用的场景
获取旧数据或者只想获取一次数据供应用程序使用,这种情况用HTTP即可。
如果仅加载一次数据,RESTful Web 服务足以从服务器获取数据。
WebSocket 断线重连
为什么需要 WebSocket 心跳
心跳:客户端定时给服务端发送消息,证明客户端是在线的,如果超过一定时间没有发送就是离线了。
WebSocket 是长连接,实际网络中存在"隐性断开"的场景,比如网络波动,防火墙/代理超时,客户端异常退出等,心跳机制核心目的是主动检测连接的活性,及时发现断开的连接,清理无效资源。
心跳是单向/双向的定时交互,这里以"客户端主动发心跳,服务端校验"为例。
有几种不同的设计方案,有的是在客户端设置定时器:
- 客户端每隔一个时间间隔发送一个探测包给服务端,
- 客户端发包时启动一个超时定时器,
- 服务端接收到检测包,回应一个包,
- 如果客户端接收到服务端的应答包,则说明服务端正常,删除超时定时器,
- 如果客户端的超时定时器超时,依然没有收到应答包,说明服务器挂了。
