【小程序websocket最佳实践,有心跳和断线重连】
小程序websocket最佳实践,有心跳和断线重连
封装了WebSocketHandler类,用于管理websocket链接,保证链接的稳定和可靠,该类主要适用于小程序,但其设计思想和方法也适用于其他平台。
export default class WebSocketHandler {
constructor() {
// 获取环境变量
this.env = wx.getStorageSync('env');
// 生产环境的 WebSocket 地址
this.prod = 'www.xx.com';
// 开发环境的 WebSocket 地址
this.dev = 'www.xx.com';
// 根据环境变量选择 WebSocket 地址
this.path = `${(this.env == 'pro' || !this.env) ? this.prod : this.dev}`;
// 构建 WebSocket URL
this.url = `wss://${this.path}/websocket`;
// 重连次数限制
this.limit = 0;
// WebSocket 连接状态标志
this.isClose = true;
// 存储未发送的消息
this.preParams = '';
// 重连锁
this.lockReconnect = false;
// 重连定时器
this.timer = null;
// 心跳检测机制
this.heartCheck = {
timeout: 3000, // 心跳检测间隔
timeoutObj: null, // 客户端定时器
serverTimeoutObj: null, // 服务端定时器
reset: function () {
// 重置定时器
clearTimeout(this.timeoutObj);
clearTimeout(this.serverTimeoutObj);
return this;
},
start: () => {
// 启动心跳检测
this.heartCheck.timeoutObj = setTimeout(() => {
// 发送 PING 消息
wx.sendSocketMessage({
data: "PING",
success() {
// 发送成功
}
});
// 设置服务端定时器,如果超时则关闭连接
this.heartCheck.serverTimeoutObj = setTimeout(() => {
wx.closeSocket();
}, this.heartCheck.timeout);
}, this.heartCheck.timeout);
}
};
// 绑定 WebSocket 事件处理函数
this._onSocketOpen();
this._onSocketMessage();
this._onSocketError();
this._onSocketClose();
}
/**
* 创建 WebSocket 连接
* @param {Object} options - 建立连接时需要的配置信息
* @param {string} options.userId - 用户 ID
* @param {string} options.agentId - 代理 ID
*/
connect({ userId}) {
if (!this.isClose) {
logger.info('WebSocket is already connected or connecting.');
return;
}
wx.connectSocket({
url: `${this.url}/${userId}`,
header: {
'content-type': 'application/json'
},
method: 'POST'
});
}
/**
* 发送消息
* @param {Object} params - 要发送的消息参数
* @returns {Promise} - 返回一个 Promise,表示消息发送的结果
*/
send(params) {
return new Promise((resolve, reject) => {
const cruParams = JSON.stringify(params);
if (this.isClose) {
this.preParams = params;
return;
}
logger.debug(`send---`, JSON.stringify(cruParams));
wx.sendSocketMessage({
data: cruParams,
success: (res) => {
logger.debug('sendSocketMessage', JSON.stringify(res));
resolve({ res });
},
fail: (res) => {
logger.error('sendSocketMessage', JSON.stringify(res));
reject(res);
},
});
});
}
/**
* 关闭 WebSocket 连接
*/
closeConnection() {
this.isClose = true;
wx.closeSocket();
}
/**
* 处理 WebSocket 错误事件
*/
_onSocketError() {
wx.onSocketError((res) => {
this.isClose = true;
logger.error('WebSocket连接打开失败,请检查!', JSON.stringify(res));
this.reconnect();
});
}
/**
* 处理 WebSocket 关闭事件
*/
_onSocketClose() {
wx.onSocketClose((res) => {
this.isClose = true;
logger.error('WebSocket 已关闭!', JSON.stringify(res));
this.reconnect();
});
}
/**
* 处理 WebSocket 打开事件
*/
_onSocketOpen() {
wx.onSocketOpen(() => {
logger.debug('WebSocket连接已打开!');
this.heartCheck.reset().start();
if (this.isClose && this.preParams) {
logger.info('重新发送WebSocket消息!');
wx.sendSocketMessage({
data: JSON.stringify(this.preParams),
success: () => {
logger.debug('重新发送--sendSocketMessage', JSON.stringify(this.preParams));
this.preParams = '';
},
fail: (res) => {
logger.error('重新发送--sendSocketMessage-fail', JSON.stringify(res));
},
});
} else {
wx.sendSocketMessage({
data: "PING",
});
}
this.isClose = false;
});
}
/**
* 处理 WebSocket 消息事件
*/
_onSocketMessage() {
wx.onSocketMessage((res) => {
if (res.data == "PONG") {
console.log('PONG成功');
this.heartCheck.reset().start();
}
});
}
/**
* 尝试重新连接 WebSocket
*/
reconnect() {
if (this.lockReconnect) return;
this.lockReconnect = true;
clearTimeout(this.timer);
if (this.limit < 50) {
this.timer = setTimeout(() => {
this.lockReconnect = false;
}, 300);
this.limit = this.limit + 1;
}
}
}
// 使用时
this.wsHandler = new WebSocketHandler();
app.wsHandler.send(params).then();