You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
219 lines
5.1 KiB
219 lines
5.1 KiB
/**
|
|
* WebSocket 工具类
|
|
*
|
|
* 功能说明:
|
|
* 1. 封装WebSocket连接管理
|
|
* 2. 自动重连机制
|
|
* 3. 心跳检测
|
|
* 4. 订阅管理
|
|
*
|
|
* 使用示例:
|
|
* import WebSocketClient from '@/utils/websocket'
|
|
*
|
|
* // 连接
|
|
* WebSocketClient.connect('http://localhost:8080/ws/dashboard', () => {
|
|
* console.log('连接成功')
|
|
* // 订阅主题
|
|
* WebSocketClient.subscribe('/topic/dashboard/robot-picking', (data) => {
|
|
* console.log('收到数据:', data)
|
|
* })
|
|
* })
|
|
*/
|
|
|
|
import SockJS from 'sockjs-client'
|
|
import Stomp from 'stompjs'
|
|
|
|
class WebSocketClient {
|
|
constructor() {
|
|
this.stompClient = null
|
|
this.connected = false
|
|
this.reconnectAttempts = 0
|
|
this.maxReconnectAttempts = 3000
|
|
this.reconnectInterval = 10000
|
|
this.subscriptions = new Map()
|
|
this.heartbeatInterval = null
|
|
this.url = null
|
|
this.onConnectedCallback = null
|
|
}
|
|
|
|
/**
|
|
* 连接WebSocket服务器
|
|
*
|
|
* @param {string} url WebSocket服务器地址
|
|
* @param {function} onConnected 连接成功回调
|
|
* @param {function} onError 连接错误回调
|
|
*/
|
|
connect(url, onConnected, onError) {
|
|
this.url = url
|
|
this.onConnectedCallback = onConnected
|
|
|
|
console.log('[WebSocket] 正在连接服务器...', url)
|
|
|
|
try {
|
|
const socket = new SockJS(url)
|
|
this.stompClient = Stomp.over(socket)
|
|
|
|
// 禁用调试日志(生产环境)
|
|
// 开发环境可以设置为 console.log
|
|
this.stompClient.debug = null
|
|
|
|
this.stompClient.connect(
|
|
{},
|
|
frame => {
|
|
console.log('[WebSocket] ✅ 连接成功')
|
|
this.connected = true
|
|
this.reconnectAttempts = 0
|
|
this.startHeartbeat()
|
|
|
|
if (onConnected) {
|
|
onConnected(frame)
|
|
}
|
|
},
|
|
error => {
|
|
console.error('[WebSocket] ❌ 连接失败:', error)
|
|
this.connected = false
|
|
this.handleDisconnect()
|
|
|
|
if (onError) {
|
|
onError(error)
|
|
}
|
|
}
|
|
)
|
|
} catch (error) {
|
|
console.error('[WebSocket] 连接异常:', error)
|
|
if (onError) {
|
|
onError(error)
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 订阅主题
|
|
*
|
|
* @param {string} topic 主题名称
|
|
* @param {function} callback 消息回调函数
|
|
* @returns {string} 订阅ID
|
|
*/
|
|
subscribe(topic, callback) {
|
|
if (!this.connected || !this.stompClient) {
|
|
console.warn('[WebSocket] 未连接,无法订阅主题:', topic)
|
|
return null
|
|
}
|
|
|
|
console.log('[WebSocket] 订阅主题:', topic)
|
|
|
|
const subscription = this.stompClient.subscribe(topic, message => {
|
|
try {
|
|
const data = JSON.parse(message.body)
|
|
callback(data)
|
|
} catch (error) {
|
|
console.error('[WebSocket] 解析消息失败:', error)
|
|
}
|
|
})
|
|
|
|
const subscriptionId = topic
|
|
this.subscriptions.set(subscriptionId, subscription)
|
|
|
|
return subscriptionId
|
|
}
|
|
|
|
/**
|
|
* 取消订阅
|
|
*
|
|
* @param {string} subscriptionId 订阅ID
|
|
*/
|
|
unsubscribe(subscriptionId) {
|
|
const subscription = this.subscriptions.get(subscriptionId)
|
|
if (subscription) {
|
|
subscription.unsubscribe()
|
|
this.subscriptions.delete(subscriptionId)
|
|
console.log('[WebSocket] 取消订阅:', subscriptionId)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 发送消息
|
|
*
|
|
* @param {string} destination 目标地址
|
|
* @param {object} data 消息数据
|
|
*/
|
|
send(destination, data) {
|
|
if (!this.connected || !this.stompClient) {
|
|
console.warn('[WebSocket] 未连接,无法发送消息')
|
|
return
|
|
}
|
|
|
|
this.stompClient.send(destination, {}, JSON.stringify(data))
|
|
}
|
|
|
|
/**
|
|
* 断开连接
|
|
*/
|
|
disconnect() {
|
|
if (this.stompClient) {
|
|
this.stompClient.disconnect(() => {
|
|
console.log('[WebSocket] 已断开连接')
|
|
})
|
|
this.connected = false
|
|
this.stopHeartbeat()
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 处理断开连接
|
|
*/
|
|
handleDisconnect() {
|
|
this.connected = false
|
|
this.stopHeartbeat()
|
|
|
|
// 自动重连
|
|
if (this.reconnectAttempts < this.maxReconnectAttempts) {
|
|
this.reconnectAttempts++
|
|
console.log(`[WebSocket] 尝试重连 (${this.reconnectAttempts}/${this.maxReconnectAttempts})...`)
|
|
|
|
setTimeout(() => {
|
|
if (this.url && this.onConnectedCallback) {
|
|
this.connect(this.url, this.onConnectedCallback)
|
|
}
|
|
}, this.reconnectInterval)
|
|
} else {
|
|
console.error('[WebSocket] 达到最大重连次数,放弃重连')
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 启动心跳检测
|
|
*/
|
|
startHeartbeat() {
|
|
this.heartbeatInterval = setInterval(() => {
|
|
if (this.connected && this.stompClient) {
|
|
// 发送心跳消息
|
|
// 注意:如果后端没有心跳接口,可以注释掉这行
|
|
// this.send('/app/heartbeat', { timestamp: Date.now() })
|
|
}
|
|
}, 30000) // 每30秒发送一次心跳
|
|
}
|
|
|
|
/**
|
|
* 停止心跳检测
|
|
*/
|
|
stopHeartbeat() {
|
|
if (this.heartbeatInterval) {
|
|
clearInterval(this.heartbeatInterval)
|
|
this.heartbeatInterval = null
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 检查连接状态
|
|
*
|
|
* @returns {boolean} 是否已连接
|
|
*/
|
|
isConnected() {
|
|
return this.connected
|
|
}
|
|
}
|
|
|
|
// 导出单例
|
|
export default new WebSocketClient()
|
|
|