|
@@ -5,13 +5,24 @@ import { getObjValue, isNullES } from 'js-fast-way'
|
|
|
class HcSocket {
|
|
|
|
|
|
constructor(data, change) {
|
|
|
- this.limit = 30
|
|
|
- this.interval = 10000
|
|
|
+ this.limit = website.socketLimit
|
|
|
+ this.interval = website.socketInterval
|
|
|
this.param = getObjValue(data)
|
|
|
this.onChange = change
|
|
|
this.socket = null
|
|
|
- this.create()
|
|
|
- //this.moreTabs()
|
|
|
+ this.isReconnecting = false
|
|
|
+ this.connectionStatus = 'disconnected'
|
|
|
+ this.isManualClosed = false
|
|
|
+ if (this.validateParams()) {
|
|
|
+ this.create()
|
|
|
+ //this.handleMultipleTabs()
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ validateParams() {
|
|
|
+ const { projectId, contractId, userId } = this.param
|
|
|
+ const { socket: socketUrl, clientId } = website
|
|
|
+ return !(isNullES(socketUrl) || isNullES(clientId) || isNullES(projectId) || isNullES(contractId) || isNullES(userId))
|
|
|
}
|
|
|
|
|
|
//创建连接
|
|
@@ -19,39 +30,52 @@ class HcSocket {
|
|
|
const { projectId, contractId, userId } = this.param
|
|
|
const { socket: socketUrl, clientId } = website
|
|
|
|
|
|
- if (isNullES(socketUrl) || isNullES(clientId) || isNullES(projectId) || isNullES(contractId) || isNullES(userId)) {
|
|
|
- return
|
|
|
- }
|
|
|
-
|
|
|
- //拼接URL并登录socket
|
|
|
const url = `${socketUrl}/blade-${clientId}/${projectId}/${contractId}/${userId}`
|
|
|
this.socket = new WebSocket(url)
|
|
|
|
|
|
- this.socket.onopen = ()=> {
|
|
|
- console.log('WebSocket 链接成功')
|
|
|
- }
|
|
|
+ this.socket.onopen = this.handleOpen.bind(this)
|
|
|
+ this.socket.onclose = this.handleClose.bind(this)
|
|
|
+ this.socket.onmessage = this.handleMessage.bind(this)
|
|
|
+ this.socket.onerror = this.handleError.bind(this)
|
|
|
+ }
|
|
|
+
|
|
|
+ handleOpen() {
|
|
|
+ console.log('WebSocket 链接成功')
|
|
|
+ this.connectionStatus = 'connected'
|
|
|
+ this.isManualClosed = false
|
|
|
+ }
|
|
|
|
|
|
- this.socket.onclose = async () => {
|
|
|
- console.log('WebSocket 链接已断开')
|
|
|
- await this.reconnect()
|
|
|
+ handleClose() {
|
|
|
+ console.log('WebSocket 链接已断开')
|
|
|
+ this.connectionStatus = 'disconnected'
|
|
|
+ if (!this.isManualClosed) {
|
|
|
+ this.reconnect()
|
|
|
}
|
|
|
+ }
|
|
|
|
|
|
- this.socket.onmessage = ({ data })=> {
|
|
|
- try {
|
|
|
- const parsedData = JSON.parse(data)
|
|
|
- if (typeof this.onChange === 'function') {
|
|
|
- this.onChange(parsedData)
|
|
|
- }
|
|
|
- } catch (error) {
|
|
|
- console.error('解析 WebSocket 数据时出错:', error)
|
|
|
+ handleMessage({ data }) {
|
|
|
+ try {
|
|
|
+ const parsedData = JSON.parse(data)
|
|
|
+ if (typeof this.onChange === 'function') {
|
|
|
+ this.onChange(parsedData)
|
|
|
}
|
|
|
+ } catch (error) {
|
|
|
+ console.error('解析 WebSocket 数据时出错:', error)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ handleError(error) {
|
|
|
+ console.error('WebSocket 错误:', error)
|
|
|
+ this.connectionStatus = 'error'
|
|
|
+ }
|
|
|
+
|
|
|
//发送消息
|
|
|
- async send(data) {
|
|
|
- if (!this.isSocket()) return
|
|
|
- this.socket.send(data)
|
|
|
+ send(data) {
|
|
|
+ if (this.isSocket()) {
|
|
|
+ this.socket.send(data)
|
|
|
+ } else {
|
|
|
+ console.warn('消息发送失败')
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
//链接是否存在
|
|
@@ -61,31 +85,54 @@ class HcSocket {
|
|
|
|
|
|
//尝试重连
|
|
|
async reconnect(attempts = 0) {
|
|
|
- if (attempts >= this.limit) return
|
|
|
- if (isNullES(this.socket) || this.socket.readyState === WebSocket.CLOSED) {
|
|
|
+ if (this.isReconnecting || this.isManualClosed) return
|
|
|
+ this.isReconnecting = true
|
|
|
+
|
|
|
+ while (attempts < this.limit && !this.isSocket() && !this.isManualClosed) {
|
|
|
+ console.log(`重新链接中... (${attempts + 1}/${this.limit})`)
|
|
|
this.create()
|
|
|
+ await new Promise(resolve => setTimeout(resolve, this.getReconnectInterval(attempts)))
|
|
|
+ attempts++
|
|
|
}
|
|
|
- await new Promise(resolve => setTimeout(resolve, this.interval))
|
|
|
- if (!this.isSocket()) {
|
|
|
- await this.reconnect(attempts + 1)
|
|
|
+
|
|
|
+ this.isReconnecting = false
|
|
|
+ if (!this.isSocket() && !this.isManualClosed) {
|
|
|
+ console.error('达到最大重连次数后仍然失败')
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ // 指数退避策略
|
|
|
+ getReconnectInterval(attempt) {
|
|
|
+ return Math.min(1000 * Math.pow(2, attempt), 30000)
|
|
|
+ }
|
|
|
+
|
|
|
+ handleMultipleTabs() {
|
|
|
+ document.addEventListener('visibilitychange', this.handleVisibilityChange.bind(this))
|
|
|
+ }
|
|
|
+
|
|
|
// 处理多标签页问题
|
|
|
- moreTabs() {
|
|
|
- document.addEventListener('visibilitychange', () => {
|
|
|
- if (document.visibilityState === 'visible' && !this.isSocket()) {
|
|
|
- this.create()
|
|
|
- } else if (document.visibilityState === 'hidden') {
|
|
|
- this.close()
|
|
|
- }
|
|
|
- })
|
|
|
+ handleVisibilityChange() {
|
|
|
+ if (document.visibilityState === 'visible' && !this.isSocket() && !this.isManualClosed) {
|
|
|
+ this.create()
|
|
|
+ } else if (document.visibilityState === 'hidden') {
|
|
|
+ this.close()
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
//关闭或断开连接
|
|
|
close() {
|
|
|
- if (!this.isSocket()) return
|
|
|
- this.socket.close()
|
|
|
+ this.isManualClosed = true
|
|
|
+ if (this.isSocket()) {
|
|
|
+ this.socket.close()
|
|
|
+ }
|
|
|
+ this.connectionStatus = 'disconnected'
|
|
|
+ }
|
|
|
+
|
|
|
+ destroy() {
|
|
|
+ this.close()
|
|
|
+ //document.removeEventListener('visibilitychange', this.handleVisibilityChange)
|
|
|
+ this.socket = null
|
|
|
+ this.onChange = null
|
|
|
}
|
|
|
}
|
|
|
|