摘要
本文将介绍两种不同的WebSocket客户端封装方法:使用函数封装和使用类封装。我们将分析它们的实现方式、特点和适用场景,以及如何根据项目需求选择合适的封装方式。
1. 引言
WebSocket提供了全双工通信渠道,允许服务器主动发送信息给客户端。在实际开发中,封装WebSocket客户端可以简化开发流程,提高代码的可维护性和可复用性。
2. 函数封装:createWebSocket
createWebSocket
函数是一种简单的封装方式,它通过接收必要的参数来创建和管理WebSocket连接。以下是其主要特点:
- 简洁性:作为一个函数,它提供了一种快速且直接的方式来处理WebSocket连接。
- 自动重连:实现了自动重连机制,能够在连接断开时尝试重新连接。
- 心跳检测:通过定时发送心跳包来保持连接的活跃状态。
- 灵活性:提供了发送消息和关闭连接的方法,使得调用者可以灵活地控制WebSocket的生命周期。
3. 类封装:WebSocketClient
WebSocketClient
类提供了面向对象的方式来管理WebSocket连接,具有以下特性:
- 封装性:将WebSocket的创建、管理和销毁封装在一个类中,提高了代码的模块化。
- 状态管理:通过属性来管理WebSocket的状态,如是否在重连中、是否手动关闭等。
- 错误处理:拥有错误消息队列,能够在连接失败时缓存消息,并在重连成功后发送。
- 事件监听:定义了多种事件处理方法,如
onopen
、onerror
、onclose
和onmessage
,使得状态变化更加可控。
export function createWebSocket(url, onOpen, onMessage) {
if (!url) return;
let canReconnect = true;
// 避免重复连接
let lockReconnect = false;
let needReconnect = true;
let ws = null;
connect();
heartCheck();
// 连接服务端
function connect() {
ws = new WebSocket(url);
ws.onopen = function () {
if (onOpen) {
onOpen();
}
};
ws.onmessage = function (msg) {
if (msg && msg.data) {
let response = JSON.parse(msg.data);
//console.log(response);
if (onMessage) {
onMessage(response);
} else {
canReconnect = false;
}
}
};
ws.onerror = function () {
console.log("链接错误");
reconnect();
};
ws.onclose = function () {
console.log("close");
if (!needReconnect) {
ws.close();
} else {
reconnect();
}
};
}
// 重新连接服务端
function reconnect() {
if (!canReconnect || lockReconnect) {
return;
}
console.log("reconnect", "reconnect");
lockReconnect = true;
setTimeout(function () {
connect();
lockReconnect = false;
}, 5000);
}
// 保持心跳连接
function heartCheck() {
if (ws && ws.readyState === 1) {
ws.send(
JSON.stringify({
id: new Date().getTime(),
method: "PING",
})
);
}
setTimeout(function () {
heartCheck();
}, 15000);
}
// 发送websocket消息
function sendMessage(message) {
if (ws && ws.readyState === 1) {
ws.send(message);
} else {
console.log("WebSocket is not open");
}
}
// 关闭websocket连接
function closeWs() {
needReconnect = false;
ws.close();
}
return {
ws,
sendMessage,
closeWs,
};
}
class WebSocketClient {
// 要连接的URL
url
// 一个协议字符串或一个协议字符串数组。
// 这些字符串用来指定子协议,这样一个服务器就可以实现多个WebSocket子协议
protocols
// WebSocket 实例
ws
// 是否在重连中
isReconnectionLoading = false
// 延时重连的 id
timeId = null
// 是否是用户手动关闭连接
isCustomClose = false
// 错误消息队列
errorStack = []
constructor(url, protocols) {
this.url = url;
this.protocols = protocols;
this.ws = null;
this.onOpen = null;
this.onMessage = null;
this.connect();
}
connect() {
if ("WebSocket" in window) {
// 实例化
this.ws = new WebSocket(this.url, this.protocols);
// 监听事件
this.onopen();
this.onerror();
this.onclose();
} else {
console.log("你的浏览器不支持 WebSocket");
}
}
// 监听成功
onopen() {
this.ws.onopen = () => {
console.log(this.ws, "onopen");
// 发送成功连接之前所发送失败的消息
this.errorStack.forEach((message) => {
this.send(message);
});
this.errorStack = [];
this.isReconnectionLoading = false;
};
}
// 监听错误
onerror() {
this.ws.onerror = (err) => {
console.log(err, "onerror");
this.reconnection();
this.isReconnectionLoading = false;
};
}
// 监听关闭
onclose() {
this.ws.onclose = () => {
console.log("onclose");
// 用户手动关闭的不重连
if (this.isCustomClose) return;
this.reconnection();
this.isReconnectionLoading = false;
};
}
// 接收 WebSocket 消息
async onmessage(fn) {
this.ws.onmessage = (event) => {
try {
const data = JSON.parse(event.data)
fn(data )
} catch (error) {
console.log(error, 'error')
}
}
}
// 重连
reconnection() {
// 防止重复
if (this.isReconnectionLoading) return
this.isReconnectionLoading = true
clearTimeout(this.timeId)
this.timeId = setTimeout(() => {
this.createWs()
}, 3000)
}
// 发送消息
send(message) {
// 连接失败时的处理
if (this.ws.readyState !== 1) {
//没有发送成功的信息保存下来,链接成功后重新发送
this.errorStack.push(message)
return
}
this.ws.send(message)
}
// 手动关闭
close() {
this.isCustomClose = true
this.ws.close()
}
// 手动开启
start() {
this.isCustomClose = false
this.reconnection()
}
// 销毁
destroy() {
this.close()
this.ws = null
this.errorStack = null
this.eventCenter = null
}
}