最近项目在做消息推送,所以就封装了个 websocket 帮助类,以此来减少重复代码。
ws.js 完整代码如下:
export default class WS {
// 构造函数
constructor(url, messageHandler, maxReconnectAttempts = 500) {
this.url = url;
this.ws = null;
this.messageHandler = messageHandler; // 外部传入的消息处理函数
this.reconnectAttempts = 0;
this.maxReconnectAttempts = maxReconnectAttempts; // 最大重连次数
this.connect()
}
// 连接
connect() {
this.unBindEvent();
this.ws = new WebSocket(this.url);
this.bindEvent();
}
// 绑定事件
bindEvent () {
this.ws.onopen = () => {
this.reconnectAttempts = 0;
console.log('Websocket 连接成功');
}
this.ws.onmessage = (e) => {
if (this.messageHandler && typeof this.messageHandler === 'function') {
this.messageHandler(e.data);
}
}
this.ws.onclose = () => {
console.log('Websocket 连接断开,尝试重连...');
if (this.reconnectAttempts < this.maxReconnectAttempts) {
setTimeout(() => { this.connect() }, 3000);
this.reconnectAttempts++;
} else {
console.error('达到最大重连次数,放弃连接');
}
}
this.ws.onerror = (e) => {
console.error('Websocket 连接错误', e);
}
}
// 清空绑定的事件,避免内存泄漏
unBindEvent () {
if (this.ws) {
this.ws.onopen = null;
this.ws.onclose = null;
this.ws.onerror = null;
this.ws.onmessage = null;
this.ws.close();
}
}
// 发送消息
sendMessage (message) {
if (this.ws.readyState === WebSocket.OPEN) {
this.ws.send(JSON.stringify(message)); // 数据的发送和接收都是字符串形式
} else {
console.error('连接未就绪');
}
}
// 关闭,断开连接
disconnect() {
this.ws.close();
}
}
在 VUE 中的使用示例
import WS from 'common/ws.js'
export default {
created () {
this.initSocket()
}
methods: {
initSocket () {
// 回调函数
const handle = (data) => {
try {
// 拿到后端推送过来的数据,并转为 JSON
const dt = JSON.parse(data);
// do something...
} catch (e) {
console.log('data', data);
}
}
// 创建实例对象,进行连接
// 并且通过连接地址传参(uid),传入回调函数(handle)
const ws = new WS(`ws://此处填写您的IP地址或域名:端口号?uid=${this.user_id}`, handle);
}
}
}
关于 WebSocket 的一个知识点
问:WebSocket 重新连接,必须重新创建实例吗?可以用上一次创建的实例重连吗?
答:在 WebSocket 中,重新连接必须创建新的实例,无法直接复用上一次创建的实例。这是因为一旦 WebSocket 连接关闭(readyState 变为 CLOSED),该实例的底层连接已被销毁,无法再次打开。
WebSocket 实例的生命周期
WebSocket 对象的状态(readyState)分为
1- CONNECTING(0):连接尚未建立。
2- OPEN(1):连接已建立,可通信。
3- CLOSING(2):连接正在关闭。
4- CLOSED(3):连接已关闭或无法打开。
当连接关闭(CLOSED)后,实例的底层资源已被释放,无法通过任何 API 重新激活。此时调用 send() 会抛出错误,且无法重新建立连接。
实例绑定的连接不可复用
WebSocket 实例与底层 TCP 连接是绑定的。连接断开后,TCP 通道已失效,必须重新发起握手流程(即创建新实例)。