文章目录
1. WebSocket 介绍
基本概念
WebSocket 是一种基于 TCP 的全双工通信协议,允许客户端和服务器建立持久连接,实现高效实时数据交换。
全双工通信:客户端和服务器可随时主动发送数据,无需遵循传统 HTTP 的 "请求-响应" 模式
握手过程
客户端发起握手请求:客户端通过普通的 HTTP 请求向服务器发起 WebSocket 握手请求,这个请求包含了一些特殊的头部,在控制台的请求标头可以看到,例如:Upgrade:websocket、Connection:Upgrade,以及一个随机的 sec-websocket-key 头部。这些头部告诉服务器这是一个 WebSocket 握手请求,并且要升级到 WebSocket 协议
服务器响应握手请求:服务器收到握手请求后,如果支持 WebSocket 协议,就会发送一个 HTTP 101 状态码,表示同意升级到 WebSocket 协议。响应头中也会包含类似的特殊头部,如:Upgrade:websocket、Connection:Upgrade,以及一个经过计算的 Sec-Websocket-Accept 头部,用于验证握手请求的合法性
握手成功,建立连接:一旦客户端收到服务器的握手响应,并验证了其中信息,握手过程就完成了,此时客户端和服务器之间就建立了 WebSocket 连接,可以开始进行实时的双向通信
在握手成功后,客户端和服务器就可以通过 Websocket 连接发送和接收数据,而不再需要普通的 HTTP 请求和响应。这种实时的双向通信方式大大提高了 Web 应用程序的交互性和实时性
2. WebSocket API
在浏览器中,JavaScript 提供了原生的 WebSocket API,特别适用于实时应用,如聊天、数据推送等场景。
创建连接
javascript
// 创建一个连接,连接失败时会抛出错误(加密协议使用 wss)
const ws = new WebSocket("ws://127.0.0.1:2345");
事件监听
javascript
// 监听连接事件(连接成功)
ws.onopen = function (e) {
console.log("WebSocket 连接成功");
console.log(e);
}
// 监听消息事件(收到服务端消息)
ws.onmessage = function (e) {
console.log("收到服务端的消息:" + e.data);
console.log(e);
}
// 监听错误事件(连接发生错误)
ws.onerror = function (e) {
console.log("WebSocket 发生错误");
console.log(e);
}
// 监听关闭事件(连接关闭)
ws.onclose = function (e) {
console.log('WebSocket 连接已关闭');
console.log(e);
}
发送消息
javascript
// 支持的参数类型 String、Blob、ArrayBuffer,一般都使用 String
ws.send('hello,world')
// 实际开发,通常发送的是 json 字符串格式
const data = { type: 'chat', mode: 'text', content: '...' }
ws.send(JSON.stringify(data))
socket 对象属性
ws.readyState 表示 socket 的连接状态,它的四个值都是 WebSocket 对象的属性
- CONNECTING:0 正在尝试建立连接
- OPEN:1 连接成功
- CLOSING:2 正在关闭连接
- CLOSED:3 连接是关闭状态
javascript
const ws = new WebSocket("ws://127.0.0.1:2345");
console.log('readyState: ' + ws.readyState);
通过以下命令,可以发现 CONNECTING、OPEN、CLOSING、CLOSED 都是 WebSocket 对象的属性
javascript
console.dir(WebSocket);
所以,我们可以通过 WebSocket 的属性来判断 socket 的状态,下面代码在重连机制中就可以用到
javascript
if (ws.readyState === WebSocket.CLOSED) {
// ...
}
socket 对象方法
- ws.send() 发送数据到服务端
- ws.close() 关闭连接
当客户端想要主动关闭连接时,可以调用 ws.close() 关闭
javascript
ws.close()
3. 心跳检测
WebSocket 是长连接协议,但网络中间设备可能因超时关闭长时间无数据传输的连接。心跳检测通过客户端和服务端定时交换心跳包,模拟活跃通信,防止连接被意外中断。
简而言之:心跳检测的作用是保持连接活跃,避免因长时间无数据传输而被关闭连接
javascript
// 定时器 id
let heartbeatTimer = null
// 开始发送心跳包,发送任意数据均可
function heartbeatStart(time = 3000) {
heartbeatTimer = setInterval(() => {
ws.send(JSON.stringify({ type: 'heartbeat' }))
}, time)
}
// 关闭定时器,取消定时发送心跳包
function heartbeatClear() {
if (heartbeatTimer) {
clearInterval(heartbeatTimer)
heartbeatTimer = null
}
}
连接成功后开始发送心跳包,关闭连接后停止发送心跳包,代码示例:
javascript
// 监听连接事件(连接成功)
ws.onopen = function (e) {
console.log("WebSocket 连接成功");
// 开启心跳检测
heartbeatStart()
}
// 监听关闭事件(连接关闭)
ws.onclose = function (e) {
console.log('WebSocket 连接已关闭');
// 关闭心跳检测
heartbeatClear()
}
4. 重连机制
WebSocket 重连机制是确保长连接稳定性的关键,比如:在网络波动或服务端重启时自动恢复连接
重连机制应该在连接断开时进行,我们要区分正常断开还是出现异常被迫断开,以避免无限重连
重连方法的核心逻辑:异常断开连接时,执行重连机制尝试重新连接,重新连接成功后清除定时器
javascript
let ws = new WebSocket("ws://127.0.0.1:2345");
let reconnectTimer = null
let reconnectInterval = 5000
function reconnect() {
if (ws.readyState === WebSocket.CLOSED) {
ws = new WebSocket("ws://127.0.0.1:2345");
}
}
// 监听连接事件(连接成功)
ws.onopen = () => {
if (reconnectTimer) {
clearInterval(reconnectTimer)
reconnectInterval = null
}
}
// 监听关闭事件(连接关闭)
ws.onclose = (event) => {
if (event.code === 1000) {
// 正常关闭,不重连
} else {
// 异常断开,触发重连逻辑
console.log('正在尝试重新连接...');
reconnectTimer = setInterval(reconnect, reconnectInterval)
}
}
在上诉代码中,可发现存在问题,事件监听只能监听到首次连接,无法监听到重新连接后的事件
因为首次进入页面后变量 ws 的值已经确定,事件监听已经完成,重新连接后 ws 变量值变了,事件监听也失效了
此处只是提供一个思路,实际开发中根据项目场景自行修改,代码结构调整后的示例:
javascript
let ws = new WebSocket("ws://127.0.0.1:2345");
let reconnectTimer = null
let reconnectInterval = 5000
function reconnect() {
if (ws.readyState === WebSocket.CLOSED) {
ws = new WebSocket("ws://127.0.0.1:2345");
// 重连后,调用事件监听方法,监听新的 ws 对象
socketEvent()
}
}
socketEvent()
function socketEvent() {
ws.onopen = () => { }
ws.onmessage = (e) => { }
ws.onclose = (event) => { }
}
5. 八股文问题
1、WebSocket 和 HTTP 的主要区别是什么?
WebSocket 是全双工通信模式,而 HTTP 是 "请求-响应" 模式
2、WebSocket 如何保证数据传输的安全性?
WebSocket 使用 wss(WebSocket Secure)协议,它是 WebSocket 协议的加密版本,相当于 HTTPS
3、WebSocket 的心跳检测有什么用?
心跳检测用于保持连接的活跃性,避免长时间无数据传输被关闭连接,还可以用于检测对方是否在线
4、WebSocket 连接过程中的状态码有哪些?
WebSocket 连接过程中的状态码有:CONNECTING(0)、OPEN(1)、CLOSING (2)、CLOSED(3)