|
@@ -1,35 +1,59 @@
|
|
|
// src/utils/socket.js
|
|
// src/utils/socket.js
|
|
|
import { ElMessage } from 'element-plus'
|
|
import { ElMessage } from 'element-plus'
|
|
|
import { ROOTPATH2 } from '../comment/httpUrl.js'
|
|
import { ROOTPATH2 } from '../comment/httpUrl.js'
|
|
|
-var WSS = null // 全局唯一的 WebSocket 实例
|
|
|
|
|
|
|
+
|
|
|
|
|
+let WSS = null // 全局唯一 WebSocket 实例
|
|
|
|
|
+let heartTimer = null // 心跳定时器
|
|
|
|
|
+let reconnectTimer = null // 重连定时器
|
|
|
|
|
+let reconnectCount = 0 // 当前重连次数
|
|
|
|
|
+const MAX_RECONNECT = 10 // 最大重连次数
|
|
|
|
|
+const RECONNECT_INTERVAL = 3000 // 重连间隔 3 秒
|
|
|
|
|
+let manuallyClosed = false
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
* 连接 WebSocket
|
|
* 连接 WebSocket
|
|
|
* @param {string} userId 当前用户 ID
|
|
* @param {string} userId 当前用户 ID
|
|
|
|
|
+ * @param {object} store Vuex store 实例
|
|
|
|
|
+ * @param {object} router Vue Router 实例
|
|
|
*/
|
|
*/
|
|
|
export function connectSocket(userId, store, router) {
|
|
export function connectSocket(userId, store, router) {
|
|
|
if (!userId) return
|
|
if (!userId) return
|
|
|
- // 若已有连接则关闭
|
|
|
|
|
|
|
+ manuallyClosed = false
|
|
|
|
|
+
|
|
|
|
|
+ // 若已有连接则关闭旧连接
|
|
|
if (WSS) {
|
|
if (WSS) {
|
|
|
- try {
|
|
|
|
|
- WSS.close()
|
|
|
|
|
- } catch { }
|
|
|
|
|
|
|
+ try { WSS.close() } catch { }
|
|
|
}
|
|
}
|
|
|
- let tabId = sessionStorage.getItem('tabId');
|
|
|
|
|
|
|
+
|
|
|
|
|
+ // 生成 tabId
|
|
|
|
|
+ let tabId = sessionStorage.getItem('tabId')
|
|
|
if (!tabId) {
|
|
if (!tabId) {
|
|
|
- tabId = Date.now() + '_' + Math.random().toString(36).slice(2);
|
|
|
|
|
- sessionStorage.setItem('tabId', tabId);
|
|
|
|
|
|
|
+ tabId = Date.now() + '_' + Math.random().toString(36).slice(2)
|
|
|
|
|
+ sessionStorage.setItem('tabId', tabId)
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
const wsUrl = `${ROOTPATH2}${userId}?tabId=${tabId}`
|
|
const wsUrl = `${ROOTPATH2}${userId}?tabId=${tabId}`
|
|
|
WSS = new WebSocket(wsUrl)
|
|
WSS = new WebSocket(wsUrl)
|
|
|
- console.log('🔗 WebSocket 连接中:', wsUrl)
|
|
|
|
|
- console.log(WSS)
|
|
|
|
|
|
|
+ console.log('WebSocket 连接中:', wsUrl)
|
|
|
|
|
+
|
|
|
|
|
+ /** 连接成功 */
|
|
|
WSS.onopen = () => {
|
|
WSS.onopen = () => {
|
|
|
- console.log('✅ WebSocket 已连接:', wsUrl)
|
|
|
|
|
|
|
+ console.log('WebSocket 已连接:', wsUrl)
|
|
|
|
|
+ reconnectCount = 0
|
|
|
|
|
+
|
|
|
|
|
+ // 启动心跳
|
|
|
|
|
+ if (heartTimer) clearInterval(heartTimer)
|
|
|
|
|
+ heartTimer = setInterval(() => {
|
|
|
|
|
+ if (WSS && WSS.readyState === WebSocket.OPEN) {
|
|
|
|
|
+ WSS.send('HeartBeat')
|
|
|
|
|
+ }
|
|
|
|
|
+ }, 5000)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ /** 接收消息 */
|
|
|
WSS.onmessage = (event) => {
|
|
WSS.onmessage = (event) => {
|
|
|
- if (event.data === 'HeartBeat') return
|
|
|
|
|
|
|
+ console.log('收到消息:', event.data)
|
|
|
|
|
+ if (event.data === 'HeartBeat' || event.data === 'Pong') return
|
|
|
let msg
|
|
let msg
|
|
|
try {
|
|
try {
|
|
|
msg = JSON.parse(event.data)
|
|
msg = JSON.parse(event.data)
|
|
@@ -37,14 +61,13 @@ export function connectSocket(userId, store, router) {
|
|
|
console.warn('非 JSON 消息:', event.data)
|
|
console.warn('非 JSON 消息:', event.data)
|
|
|
return
|
|
return
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
if (msg.type === 'kickOut') {
|
|
if (msg.type === 'kickOut') {
|
|
|
ElMessage({
|
|
ElMessage({
|
|
|
message: msg.content || '您的账号已在其他设备登录',
|
|
message: msg.content || '您的账号已在其他设备登录',
|
|
|
type: 'warning',
|
|
type: 'warning',
|
|
|
duration: 1500
|
|
duration: 1500
|
|
|
})
|
|
})
|
|
|
-
|
|
|
|
|
- // 清空本地存储
|
|
|
|
|
localStorage.removeItem('token')
|
|
localStorage.removeItem('token')
|
|
|
localStorage.removeItem('userId')
|
|
localStorage.removeItem('userId')
|
|
|
localStorage.removeItem('userType')
|
|
localStorage.removeItem('userType')
|
|
@@ -55,12 +78,30 @@ export function connectSocket(userId, store, router) {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ /** 错误处理 */
|
|
|
WSS.onerror = (err) => {
|
|
WSS.onerror = (err) => {
|
|
|
- console.error('❌ WebSocket 错误:', err)
|
|
|
|
|
|
|
+ console.error('WebSocket 错误:', err)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ /** 连接关闭 */
|
|
|
WSS.onclose = () => {
|
|
WSS.onclose = () => {
|
|
|
- console.log('🔌 WebSocket 已关闭')
|
|
|
|
|
|
|
+ console.log('WebSocket 已关闭')
|
|
|
|
|
+
|
|
|
|
|
+ if (heartTimer) clearInterval(heartTimer)
|
|
|
|
|
+ WSS = null
|
|
|
|
|
+
|
|
|
|
|
+ // 自动重连
|
|
|
|
|
+ if (!manuallyClosed) {
|
|
|
|
|
+ if (reconnectCount < MAX_RECONNECT) {
|
|
|
|
|
+ reconnectCount++
|
|
|
|
|
+ console.warn(`WebSocket 断开,尝试第 ${reconnectCount} 次重连...`)
|
|
|
|
|
+ reconnectTimer = setTimeout(() => {
|
|
|
|
|
+ connectSocket(userId, store, router)
|
|
|
|
|
+ }, RECONNECT_INTERVAL)
|
|
|
|
|
+ } else {
|
|
|
|
|
+ console.error('达到最大重连次数,停止重连')
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -74,14 +115,17 @@ export function sendSocketMsg(data) {
|
|
|
if (WSS && WSS.readyState === WebSocket.OPEN) {
|
|
if (WSS && WSS.readyState === WebSocket.OPEN) {
|
|
|
WSS.send(data)
|
|
WSS.send(data)
|
|
|
} else {
|
|
} else {
|
|
|
- console.warn('⚠️ WebSocket 未连接,无法发送')
|
|
|
|
|
|
|
+ console.warn('WebSocket 未连接,尝试重连后再发送')
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/** 关闭连接 */
|
|
/** 关闭连接 */
|
|
|
export function closeSocket() {
|
|
export function closeSocket() {
|
|
|
|
|
+ manuallyClosed = true
|
|
|
|
|
+ if (heartTimer) clearInterval(heartTimer)
|
|
|
|
|
+ if (reconnectTimer) clearTimeout(reconnectTimer)
|
|
|
if (WSS) {
|
|
if (WSS) {
|
|
|
- WSS.close()
|
|
|
|
|
|
|
+ try { WSS.close() } catch { }
|
|
|
WSS = null
|
|
WSS = null
|
|
|
}
|
|
}
|
|
|
-}
|
|
|
|
|
|
|
+}
|