websocket 重连封装
/**
* websocket 类
*
* 回调方法和原生websocket保持一致
* @param {String} url 连接地址
* @param {Boolean} isReconnect 是否包含重连机制
* 定时向后端发送 'ping' 后端收到后返回对应值,以此检测连接是否在线
* 如不在线则定时发起重连
* @param {Object} options 配置参数
* **/
class Ws {
constructor(url, isReconnect = true, options = {}) {
if (url) {
if (typeof options === 'object') {
this.opt = {
url: url, // 连接地址
// 心跳检测
isReconnect: isReconnect, // 是否开启重连
timeout: 4000, // 超时时间,超过后则发起重连
pingString: 'ping', // 检测标识
intervalStep: 3000, // 定时ping服务时间
reconnectStep: 3000 // 设置重连间隔,防止重复连接
}
this.opt = {
...this.opt,
...options
}
this.lockReconnect = false // 重连锁,避免频繁重连
this.isClose = false
this.onopen = () => {} // 创建打开连接回调
this.onmessage = () => {} // 收到消息通知的回调
this.onclose = () => {} // 关闭连接的回调
this.onerror = () => {} // 连接错误回调
this.timeoutObj = null // 心跳检测计时器
this.intervalObj = null // 定时发送计时器
this.reconnectTimer = null // 避免重连锁
this.createWs()
} else {
console.error('options need object')
}
} else {
console.error('ur is required')
}
}
// 创建ws
createWs() {
if (this.ws) {
this.close()
}
this.ws = new WebSocket(this.opt.url)
this.isClose = false
this.setWsData()
this.ws.onopen = () => {
if (this.opt.isReconnect) {
this.ping()
this.reset().start()
}
this.onopen()
}
this.ws.onmessage = (data) => {
if (this.opt.isReconnect && data.data === this.opt.pingString) {
this.reset().start()
} else {
this.onmessage(data)
}
}
this.ws.onclose = () => {
if (this.opt.isReconnect) this.reconnect()
this.onclose()
}
this.ws.onerror = () => {
if (this.opt.isReconnect) this.reconnect()
this.onerror()
}
}
// 设置ws参数
setWsData() {
if (this.ws) {
this.binaryType = this.ws.binaryType
this.bufferedAmount = this.ws.bufferedAmount
this.protocol = this.ws.protocol
this.readyState = this.ws.protocol
} else {
this.binaryType = ''
this.bufferedAmount = null
this.protocol = ''
this.readyState = null
}
}
// 发送消息
send(data) {
this.ws.send(data)
return this
}
// 关闭连接
close() {
this.isClose = true
this.ws.close()
return this
}
// 清除定时器
clearTimer() {
if (this.reconnectTimer) {
clearTimeout(this.reconnectTimer)
}
if (this.timeoutObj) {
clearTimeout(this.timeoutObj)
}
if (this.intervalObj) {
clearTimeout(this.intervalObj)
}
}
// 开启心跳
start() {
this.timeoutObj = setTimeout(() => {
this.reconnect()
}, this.opt.timeout)
return this
}
// 重置心跳
reset() {
clearTimeout(this.timeoutObj)
return this
}
// 重连
reconnect() {
if (this.isClose) {
this.clearTimer()
return
}
if (this.lockReconnect) return
this.lockReconnect = true
if (this.reconnectTimer) clearTimeout(this.reconnectTimer)
this.reconnectTimer = setTimeout(() => {
this.createWs()
this.lockReconnect = false
}, this.opt.reconnectStep)
return this
}
// ping
ping() {
if (this.opt.isReconnect) {
if (this.intervalObj) clearTimeout(this.intervalObj)
this.intervalObj = setTimeout(() => {
if (this.isClose) {
this.clearTimer()
} else {
try {
if (this.ws) {
this.ws.send(this.opt.pingString)
this.ping()
}
} catch (e) {
console.info('ws ping error:', e)
}
}
}, this.opt.intervalStep)
}
}
}