声明
这个工具方法并非我原创,而是在他人基础上进行的二改,原链接:
前言
这个原作者封装的大体上是没有问题的,在微信小程序内是可以完美运行的,但是在APP上就会出现bug,比如:
封装成
Class类然后使用Vue.prototype.xxx方法进行全局挂载时,微信小程序会出现挂载不上的情况(APP、H5未测试)uniapp开发的安卓APP在触发onHide生命周期后socket连接就会中断如果连接需要登录的情况下,在未登录的时候初始化的连接失败后只会触发一次重连,因为只有监听连接关闭的方法,没有监听连接失败的方法
onHide导致连接终端后使用重连方法可能会出现多条连接,此时接收消息或者发送消息会造成数据重复或方法重复执行的情况
以上只是我发现的bug,所以我的二改也是基于原作者的代码在尽量不动他的逻辑的情况下去对以上bug进行的修复优化,且不保证我的代码不会有新的bug
改动
把类拆分成了一个个对外暴露的函数,在使用时直接调用
ws_init函数,并使用变量来存储socket连接优化了重连函数的逻辑
将初始化时需要传入的链接地址以及心跳间隔时间存到项目的配置文件中,并对外暴露
目录结构
utils
├ config.js
├ websocket.jsconfig.js
import {
getToken
} from "./token"
let localServer = "127.0.0.1:8001" // 本地API
let devServer = "127.0.0.1:8001" // 测试服API
let prodServer = "127.0.0.1:8001" // 正式服API
let env = "local" // 环境:local|dev|prod
let suffix = "/home" // 接口请求URL前缀
let wsSuffix = "/socket?authorization=" // ws连接URL前缀
// baseUrl 获取接口请求URL
export function baseUrl() {
if (env == "dev") {
return `https://${devServer}${suffix}`
} else if (env == "prod") {
return `https://${prodServer}${suffix}`
} else {
return `http://${localServer}${suffix}`
}
}
// wsUrl 获取ws连接URL
export function wsUrl() {
if (env == "dev") {
return `wss://${devServer}${wsSuffix}${getToken()}`
} else if (env == "prod") {
return `wss://${prodServer}${wsSuffix}${getToken()}`
} else {
return `ws://${localServer}${wsSuffix}${getToken()}`
}
}
const sec = 1000
const min = 60 * sec
const hour = 6 * min
const day = 24 * hour
export const wsHeartBeatTime = 3 * min // 心跳间隔时间websocket.js
import {
wsHeartBeatTime,
wsUrl
} from "./config"
export var is_open_socket = false;
var data = null;
var heartbeatInterval = null;
var ws_reconnectTimeOut = null;
var socketTask = null;
export function ws_init() {
console.log("ws初始化")
is_open_socket = false //避免重复连接
data = null
//心跳检测
heartbeatInterval = null //检测服务器端是否还活着
ws_reconnectTimeOut = null //重连之后多久再次重连
try {
return ws_connectSocketInit()
} catch (e) {
console.log('catch');
is_open_socket = false
ws_reconnect();
}
}
// 进入这个页面的时候创建websocket连接【整个页面随时使用】
export function ws_connectSocketInit() {
socketTask = uni.connectSocket({
url: wsUrl(),
success: () => {
console.log("正准备建立websocket中...");
// 返回实例
return socketTask
}
});
socketTask.onOpen((res) => {
console.log("WebSocket连接正常!");
clearTimeout(ws_reconnectTimeOut)
clearInterval(heartbeatInterval)
ws_start();
// 注:只有连接正常打开中 ,才能正常收到消息
socketTask.onMessage((res) => {
// 收到消息后的操作
});
})
// 监听连接失败,这里代码我注释掉的原因是因为如果服务器关闭后,和下面的onclose方法一起发起重连操作,这样会导致重复连接
// uni.onSocketError((res) => {
// console.log('WebSocket连接打开失败,请检查!');
// this.is_open_socket = false;
// this.ws_reconnect();
// });
// 这里仅是事件监听【如果socket关闭了会执行】
socketTask.onClose(() => {
console.log("已经被关闭了")
is_open_socket = false;
ws_reconnect();
})
}
//发送消息
export function ws_send(value) {
// 注:只有连接正常打开中 ,才能正常成功发送消息
socketTask.send({
data: value,
async success() {
console.log("消息发送成功");
},
});
}
//开启心跳检测
export function ws_start() {
heartbeatInterval = setInterval(() => {
data = {
event: "ping"
}
console.log(data)
ws_send(JSON.stringify(data));
}, wsHeartBeatTime)
}
//重新连接
export function ws_reconnect() {
ws_close()
console.log("是否认为关闭", is_open_socket)
//停止发送心跳
clearInterval(heartbeatInterval)
//如果不是人为关闭的话,进行重连
if (!is_open_socket) {
ws_reconnectTimeOut = setTimeout(() => {
ws_connectSocketInit();
}, 3000)
}
}
//外部获取消息
export function ws_getMessage(callback) {
socketTask.onMessage((res) => {
return callback(res)
})
}
export function ws_close(loguot) {
// 判断是否为认为退出登录
loguot ? is_open_socket = true : ""
socketTask.close()
}使用方法
在
App.vue文件中的onLaunch生命周期内调用ws_init()函数进行连接初始化并发起连接如果
ws连接需要登录之后才能连接成功的话,请在登录成功后的代码内添加ws_close()和ws_reconnect()函数,无比要先关闭之前的连接后再进行重新,以防止产生多个连接如果是安卓
APP,请在App.vue文件内的onShow生命周期内调用ws_reconnect()函数,并在onHide生命周期内调用ws_close()函数,保证每次触发onHide生命周期后抢在ws断连之前把ws连接手动关闭,等触发onShow生命周期后再进行重连,以保证用最快速度进行重连的同时不会产生多个连接
咳咳
代码中监听连接错误和监听连接关闭两个同时使用时会造成无限重连且会创建出无限个连接的bug未修复,以后修复了再更新吧