197 lines
6.4 KiB
JavaScript
197 lines
6.4 KiB
JavaScript
class WebSocketClass {
|
||
constructor (wsUrl) {
|
||
this.successFn = {} // 成功的回掉函数
|
||
this.wsUrl = wsUrl // 请求的url
|
||
this.errorCallBackFunArr = [] // 当推送异常时执行的数组
|
||
this.isConnection = false // 判断是否支持weosocket
|
||
this.isErrorCallBack = false
|
||
this.lockReconnect = false // 避免重复连接
|
||
this.ping = null
|
||
this.sendObj = {}
|
||
this.heartCheck()
|
||
this.createWebSocket()
|
||
}
|
||
createWebSocket () {
|
||
try {
|
||
this.webSocket = new WebSocket(this.wsUrl)
|
||
this.initEventHandle()
|
||
} catch (e) {
|
||
this.errorCallBackData()
|
||
this.reconnect(this.wsUrl)
|
||
}
|
||
}
|
||
initEventHandle () {
|
||
this.webSocket.onclose = () => {
|
||
this.errorCallBackData()
|
||
this.reconnect(this.wsUrl)
|
||
}
|
||
this.webSocket.onerror = () => {
|
||
this.errorCallBackData()
|
||
this.reconnect(this.wsUrl)
|
||
}
|
||
this.webSocket.onopen = () => {
|
||
this.isConnection = true
|
||
this.isErrorCallBack = false
|
||
clearInterval(this.ping)
|
||
this.ping = setInterval(() => {
|
||
this.send('{"event": "ping"}')
|
||
}, 10000)
|
||
// 心跳检测重置
|
||
this.heartCheck.start()
|
||
}
|
||
this.webSocket.onmessage = data => {
|
||
// 如果获取到消息,心跳检测重置
|
||
// 拿到任何消息都说明当前连接是正常的
|
||
this.heartCheck.start()
|
||
this.decodeData(data)
|
||
}
|
||
}
|
||
send(cmd) {
|
||
// 只有当 webSocket.readyState 为 OPEN才发送订阅
|
||
// readyState属性返回实例对象的当前状态,共有四种。
|
||
// CONNECTING:值为0,表示正在连接。
|
||
// OPEN:值为1,表示连接成功,可以通信了。
|
||
// CLOSING:值为2,表示连接正在关闭。
|
||
// CLOSED:值为3,表示连接已经关闭,或者打开连接失败。
|
||
if (!cmd) {
|
||
return
|
||
}
|
||
// this.sendObj[cmd] && (delete clearTimeout(this.sendObj[cmd]))
|
||
if (this.webSocket.readyState === 1) { // 只有当链接打开时才进行订阅
|
||
this.webSocket.send(cmd)
|
||
}
|
||
// else if (this.webSocket.readyState === 0) { // 如果链接处于正在链接中,则进行延时订阅
|
||
// this.sendObj[cmd] = setTimeout(() => {
|
||
// this.send(cmd)
|
||
// }, 50)
|
||
// }
|
||
}
|
||
|
||
reconnect (url) {
|
||
if (this.lockReconnect) {
|
||
return
|
||
}
|
||
this.lockReconnect = true
|
||
// 没连接上会一直重连,设置延迟避免请求过多
|
||
setTimeout(() => {
|
||
this.createWebSocket(url)
|
||
this.lockReconnect = false
|
||
}, 2000)
|
||
}
|
||
errorCallBackData () {
|
||
this.isConnection = false
|
||
if (!this.isErrorCallBack && this.errorCallBackFunArr.length) {
|
||
this.isErrorCallBack = true
|
||
for (let i = 0, len = this.errorCallBackFunArr.length; i < len; i++) {
|
||
if ((typeof this.errorCallBackFunArr[i]) === 'function') {
|
||
this.errorCallBackFunArr[i]()
|
||
}
|
||
}
|
||
}
|
||
}
|
||
decodeData (data) {
|
||
if (data.data instanceof Blob) {
|
||
let blob = data.data
|
||
// js中的blob没有没有直接读出其数据的方法,通过FileReader来读取相关数据
|
||
let reader = new FileReader()
|
||
reader.readAsArrayBuffer(blob)
|
||
// 当读取操作成功完成时调用.
|
||
reader.onload = (evt) => {
|
||
if (evt.target.readyState === FileReader.DONE) {
|
||
let result = new Uint8Array(evt.target.result)
|
||
// 如果后端进行压缩数据处理(zlib),那么要引入解析zlib的js
|
||
result = (new window.Zlib.RawInflate(result)).decompress()
|
||
let strResult = ''
|
||
let length = result.length
|
||
for (let i = 0; i < length; i++) {
|
||
strResult += String.fromCharCode(result[i])
|
||
}
|
||
this.callBackData(JSON.parse(strResult))
|
||
}
|
||
}
|
||
return
|
||
}
|
||
let d = JSON.parse(data.data)
|
||
// 如果后端需要等待,则返回code:10010,过一段时间后重新订阅
|
||
if (d.code === '10010') {
|
||
let dt = {}
|
||
Object.assign(dt, d)
|
||
delete dt.code
|
||
delete dt.msg
|
||
setTimeout(() => {
|
||
this.send(JSON.stringify(dt))
|
||
}, 2000)
|
||
return
|
||
}
|
||
this.callBackData(JSON.parse(data.data))
|
||
}
|
||
callBackData (data) {
|
||
if (data instanceof Array) {
|
||
for (let i = 0; i < data.length; i++) {
|
||
this.doCallback(data[i])
|
||
}
|
||
} else if (data instanceof Object) {
|
||
if (data.hasOwnProperty('event') && data.event === 'pong') {
|
||
return
|
||
}
|
||
data.payload && (data.payload = JSON.parse(data.payload))
|
||
this.doCallback(data)
|
||
}
|
||
}
|
||
doCallback (data) {
|
||
if (data.event) {
|
||
let fn = this.successFn[data.event]
|
||
if (typeof fn === 'function') {
|
||
fn(data)
|
||
}
|
||
}
|
||
}
|
||
// 心跳检测
|
||
heartCheck () {
|
||
let that = this
|
||
this.heartCheck = {
|
||
timeout: 10000, // 10秒
|
||
timeoutObj: null,
|
||
serverTimeoutObj: null,
|
||
reset: function () {
|
||
clearTimeout(this.timeoutObj)
|
||
clearTimeout(this.serverTimeoutObj)
|
||
},
|
||
start: function () {
|
||
this.reset()
|
||
let self = this
|
||
this.timeoutObj = setTimeout(function () {
|
||
// 这里发送一个心跳,后端收到后,返回一个心跳消息
|
||
// onmessage拿到返回的心跳就说明连接正常
|
||
that.send('{"event": "ping"}')
|
||
self.serverTimeoutObj = setTimeout(function () { // 如果超过一定时间还没重置,说明后端主动断开了
|
||
// 如果onclose会执行reconnect,我们执行ws.close()就行了.如果直接执行reconnect 会触发onclose导致重连两次
|
||
that.webSocket.close()
|
||
}, self.timeout)
|
||
}, this.timeout)
|
||
}
|
||
}
|
||
}
|
||
}
|
||
function webSocketFn (url) {
|
||
let wb = new WebSocketClass(url)
|
||
return {
|
||
webSocketSend: wb.send.bind(wb),
|
||
errorCallBackFunArr: wb.errorCallBackFunArr,
|
||
successFn: wb.successFn,
|
||
isConnection: function () {
|
||
return wb.isConnection
|
||
},
|
||
getSocketFile (webSocketData, ajaxData) {
|
||
let socketFail = (isFirst) => {
|
||
if (this.isConnection()) {
|
||
webSocketData()
|
||
} else {
|
||
ajaxData()
|
||
setTimeout(socketFail, 2000)
|
||
}
|
||
}
|
||
return socketFail
|
||
}
|
||
}
|
||
} |