前言
在实时性要求极高的场景(如聊天室、金融行情、即时游戏)中,传统的 HTTP “请求-响应”模式显得捉襟见肘。WebSocket 作为 HTML5 推出的双向通信协议,成为了解决这一痛点的终极武器。
一、 什么是 WebSocket?
WebSocket 是一种在单个 TCP 连接上进行全双工通信的协议。
- 双向性:服务器可以主动向客户端推数据,客户端也可以主动向服务器发数据。
- ** 端口号**:默认端口也是80(ws)和443(wss)
- 持久性:一旦连接建立,除非主动关闭,否则连接将一直保持。
- 轻量级:数据包头部较小,减少了不必要的网络开销。
二、 连接过程:HTTP 的“变脸”艺术
WebSocket 并不是凭空产生的,它的建立依赖于 HTTP 协议的一次“升级”。
-
TCP 三次握手:首先建立基础的 TCP 连接。
-
发送 Upgrade 请求:tcp连接成功后,客户端先向服务器发送一个GET请求,服务器根据请求头中的
Connection和Upgrade,来决定是否需要使用websocket协议:Upgrade: websocketConnection: Upgrade
-
服务器响应 101:如果服务器支持,返回状态码
101 Switching Protocols。 -
协议转换:握手成功,接下来客户端和服务器可以随时主动发送消息给对方了。
三、 基础用法回顾
-
使用webSocket构造函数创建连接,指定
ws(非加密)或wss(加密)协议 -
设置相关的监听函数:
onopen:连接建立时触发onmessage:接收服务器消息(数据通过event.data获取)onerror:通信错误处理onclose:连接关闭时触发
-
设置发送与关闭方法
send()发送文本或二进制数据close()主动终止连接
const webSocket = new WebSocket('ws://localhost:5001')
//连接成功后的回调
webSocket.onopen = function () {
console.log('连接成功...')
webSocket.send('Hello!')
}
//连接失败后的回调
webSocket.onerror = function () {
console.log('连接失败...')
}
//从服务器接收到信息时的回调
webSocket.onmessage = function (event) {
console.log('接收到消息...' + event.data)
}
//连接关闭后的回调
webSocket.onclose = function () {
console.log('连接已关闭...')
}
四、 核心痛点:连接稳定性与心跳检测
1. 为什么需要心跳机制?
- 网络沉默:某些网络设备(如防火墙、代理)会自动切断长时间没有数据流动的 TCP 连接。
- 无感知断开:如果用户突然断网(如进入电梯),前端的
onclose有时不会立即触发。如果不发数据,前端可能以为还连着。
2. 解决方案:Ping/Pong 机制
客户端每隔一段时间发送一个心跳包(Ping),服务端收到消息,并且还活着的话,就会发送一个数据包(Pong)给客户端,告诉自己还活着,说明链路通畅。
五、 实战:封装一个健壮的 WebSocket类
// WebSocket类
class SocketClient {
constructor(url, options = {}) {
this.url = url;
this.ws = null;
this.lockReconnect = false; // 避免重复重连
// 配置项
this.options = {
heartbeatInterval: 20000, // 心跳间隔
reconnectInterval: 5000, // 重连间隔
...options,
};
this.heartbeatTimer = null; // 心跳定时器
this.onMessageCallback = options.onMessage || null; // 接收消息回调
this.onOpenCallback = options.onOpen || null; // 连接成功回调
this.createWebSocket();
}
// 创建连接
createWebSocket() {
this.close(false); // 清理旧实例和心跳
try {
this.ws = new WebSocket(this.url);
this.initEventHandle();
} catch (e) {
console.error("WebSocket 初始化失败:", e);
this.reConnect();
}
}
// 初始化相关监听事件
initEventHandle() {
this.ws.onopen = () => {
console.log("WebSocket 连接成功");
this.startHeartbeat();
if (this.onOpenCallback) this.onOpenCallback();
};
this.ws.onmessage = (event) => {
// 只要收到消息,就重置心跳
this.resetHeartbeat();
console.log("收到原始消息:", event.data);
// 处理业务逻辑
try {
const data = JSON.parse(event.data);
if (this.onMessageCallback) this.onMessageCallback(data);
} catch (e) {
if (this.onMessageCallback) this.onMessageCallback(event.data);
}
};
this.ws.onerror = (error) => {
console.error("WebSocket 异常:", error);
this.reConnect();
};
this.ws.onclose = (event) => {
console.log("WebSocket 已关闭:", event.code);
};
}
//发送消息
send(data) {
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
this.ws.send(
typeof data === "string" ? data : JSON.stringify(data)
);
} else {
console.warn("WebSocket 未连接,消息发送失败");
}
}
//心跳管理
startHeartbeat() {
this.heartbeatTimer = setTimeout(() => {
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
this.send({ type: "heartbeat", timestamp: Date.now() });
console.log("发送心跳...");
this.startHeartbeat(); // 递归开启下一次心跳
}
}, this.options.heartbeatInterval);
}
//重置心跳
resetHeartbeat() {
clearTimeout(this.heartbeatTimer);
this.startHeartbeat();
}
// 重连逻辑,lockReconnect是为了防止在重连时间间隔内再次连接
reConnect() {
if (this.lockReconnect) return;
this.lockReconnect = true;
console.log(
`将在 ${this.options.reconnectInterval / 1000}s 后尝试重连...`
);
setTimeout(() => {
this.lockReconnect = false;
this.createWebSocket();
}, this.options.reconnectInterval);
}
// 主动关闭, 是否永久关闭(不再重连)
close(permanent = true) {
clearTimeout(this.heartbeatTimer);
if (this.ws) {
// 如果是永久关闭,移除所有监听器防止触发重连逻辑
if (permanent) {
this.ws.onclose = null;
this.ws.onerror = null;
}
this.ws.close(); //调用ws close关闭连接
this.ws = null;
}
}
}
六、 总结与注意事项
- 安全性:在生产环境务必使用
wss://(WebSocket over TLS),防止数据被中间人窃听。 - 同源策略:WebSocket 不受同源策略限制,但服务器可以通过
Origin头部进行安全校验。 - 状态判定:在调用
send()之前,务必检查ws.readyState === WebSocket.OPEN(即 1),否则会报错。