js
export default class WebSocketClass {
constructor(options) {
// websocket对象
this.ws = null
// websocket是否关闭
this.socketStatus = null
// 消息队列,未成功发送的队列
this.socketMsgQueue = []
// 手动记录socket状态 -1:未连接, 0:连接中, 1:已连接 (由于socket存在假死状态不推荐使用readyState)
this.connectStatus = -1
// 重连定时器
this.reConnInterval = null
// 重连次数
this.RECONNECTION_NUMBER = 20
// 记录重连次数
this.reconnectionCount = 0
// 是否开启定心跳检测
this.isHeartCheck = false
// 心跳检测 定时器 初始化
this.heartTimeout = null
this.heartServerTimeout = null
// 链接websocket
this.initConnectSocket()
// 访客进入
this.visitor = options.visitor
// 消息推送
this.pushMsg = options.pushMsg
// 删除消息
this.removeChatList = options.removeChatList
// 连接/重连 拉取最新消息(可socket返回或ajax请求拉取数据)
this.reConnectGetMsg = options.reConnectGetMsg
}
// 获取socket链接地址
getWSURL() {
return 'ws://121.40.165.18:8800'
}
// 初始化 连接websocket
initConnectSocket() {
if (!Reflect.has(window, 'WebSocket')) {
return console.log('您的浏览器不支持WebSocket')
}
// 清除重连定时器
this.reConnInterval && clearInterval(this.reConnInterval)
// 判断socket状态
if (this.ws && this.ws.readyState === 1) {
this.ws.close()
this.wss = null
}
this.connectStatus = 0
this.ws = new WebSocket(this.getWsURL())
// WebSocket 连接成功后的回调函数
this.ws.addEventListener(
'open',
() => {
console.log('websocket connection successful')
this.connectStatus = 1
// 连接次数归零
this.reconnectionCount = 0
//处理未发送或者发送失败消息
this.socketMsgQueue.forEach((item) => {
// 重新发送失败的消息
this.socketSendMessage(item)
})
this.socketMsgQueue = []
this.reConnectGetMsg()
// 判断是否启动 socket 心跳检测
if (this.isHeartCheck) {
this.heartCheck()
}
},
false
)
// 监听服务器接受到信息时的回调函数
this.ws.addEventListener(
'message',
(res) => {
let message = {}
if (res.data && typeof res.data !== 'object') {
message = JSON.parse(res.data)
}
// 批量处理
if (Array.isArray(message)) {
message.forEach((item) => {
this.processMessage(item)
})
} else {
this.processMessage(message)
}
},
false
)
// WebSocket 连接关闭后的回调函数
this.ws.addEventListener(
'close',
(error) => {
console.log(`socket has been closed, ${error}`)
this.connectStatus = -1
this.reLinkSocket()
},
false
)
// WebSocket 连接失败后的回调函数
this.ws.addEventListener(
'error',
(error) => {
console.log(`socket has been error, ${error}`)
this.connectStatus = -1
this.reLinkSocket()
},
false
)
}
// 监听信息推送
processMessage(message) {
// 接受到心跳检测返回值
if (message === 'pong') {
return this.heartCheck()
}
// 访客进入
if (message.types === '访客进入') {
this.visitor(message)
}
console.log(message)
}
// 发送信息
async sendMessage(data) {
console.log(`发送消息给服务器:${data}`)
// 发送消息时,先添加到消息列表显示出来
// TODO:不能等服务器返回在显示,由于消息服务器可能存在积压状态导致消息推送过慢情况处理
this.pushMsg(premsg)
if (this.connectStatus === 1) {
return this.ws.send(data)
}
this.socketMsgQueue.push(message)
await this.closeSocket()
await this.reLinkSocket()
}
// 撤回消息
withdrawMessage(data) {
this.sendMessage(data)
// 删除消息
this.removeChatList(data)
}
/**
* @method 接收消息后格式化消息
* @param {Object} message 消息内容
* @param {Boolean} isOwn 是否属于自己发出的消息
* @return {Array} 返回格式化后消息
*/
formatMessageTalk(message, isOwn) {
// some code...
}
// 心跳包检测
heartCheck() {
// 心跳时间与收发内容以后端约定为主
this.heartTimeout && clearTimeout(this.heartTimeout)
this.heartServerTimeout && clearTimeout(this.heartServerTimeout)
this.heartTimeout = setTimeout(() => {
// 检查socket链接状态 才可发送
if (this.connectStatus === 1) {
this.ws.send('ping')
}
// 如果5s内没有返回约定值判定socket处于假死状态,重新链接socket
this.heartServerTimeout = setTimeout(async () => {
console.log('heartCheck timeout')
await this.closeSocket()
await this.reLinkSocket()
}, 5000)
}, 20000)
}
// 关闭WebSocket
closeSocket() {
if (this.ws) {
this.ws.close()
this.ws = null
// 清除重连定时器
this.reConnInterval && clearInterval(this.reConnInterval)
// 清除心跳包定时器
this.heartTimeout && clearTimeout(this.heartTimeout)
this.heartServerTimeout && clearTimeout(this.heartServerTimeout)
}
}
// 断线重连
// TODO: 严谨写法需要判断主动重连与被动重连情况
reLinkSocket() {
console.log('reconnection webscoket function')
// 清除心跳包定时器
this.heartTimeout && clearTimeout(this.heartTimeout)
this.heartServerTimeout && clearTimeout(this.heartServerTimeout)
// 清除重连定时器
this.reConnInterval && clearInterval(this.reConnInterval)
this.reConnInterval = setInterval(() => {
// 判断是否达到重连次数
if (this.reconnectionCount < RECONNECTION_NUMBER) {
this.connectSocket()
this.reconnectionCount += 1 // 重连次数
} else {
clearInterval(this.reConnInterval)
}
}, 5000)
}
}
js
const websocket = new WebSocketClass({
// 访客进入
visitor: (userInfo) => {
console.log(userInfo)
// some code...
},
// 消息推送
pushMsg: (msgdata) => {
console.log(msgdata)
// some code...
},
// 删除消息
removeChatList: (messageId) => {
console.log(messageId)
// some code...
},
// 连接/重连 拉取最新数据
reConnectGetMsg: () => {
// 拉取最新消息
},
})