A:增加前端WebSocket连接使用

This commit is contained in:
jimmy committed 2025-05-26 22:07:05 +08:00
1 parent 6d9a6ac3ee
commit 83b7ca5971
8 files changed
+2015

No files matched your search

@@ -0,0 +1,381 @@
/**
* Time:2025/5/25 20:42 29
* Name:useWebSocketEnhanced.ts
* Path:src/composables
* ProjectName:ClientApp
* Author:charlatans
*
* Il n'ya qu'un héroïsme au monde :
* c'est de voir le monde tel qu'il est et de l'aimer.
*/
// composables/useWebSocketEnhanced.ts - 增强的WebSocket管理
import { ref, onUnmounted, watch } from 'vue';
import { useWebSocketStore } from '@/stores/websocketStore';
import { WebSocketRetryManager } from '@/utils/websocketHelpers';
import type { NetworkStatus, ConnectionInfo, FirewallRule, WebSocketMessage, WebSocketCommand } from '@/types/websocket';
export class EnhancedWebSocketManager {
private ws: WebSocket | null = null;
private retryManager = new WebSocketRetryManager();
private pingTimer: number | null = null;
private dataRefreshTimer: number | null = null;
private messageHandlers = new Map<string, Function[]>();
private pendingCommands = new Map<string, { resolve: Function; reject: Function; timeout: number }>();
private store = useWebSocketStore();
constructor(private url: string = 'ws://localhost:8080') {
this.setupEventHandlers();
}
async connect(): Promise<void> {
if (this.ws?.readyState === WebSocket.OPEN) return;
this.store.updateConnectionState(false, true);
return new Promise((resolve, reject) => {
try {
this.ws = new WebSocket(this.url);
this.ws.onopen = () => {
console.log('WebSocket connected successfully');
this.store.updateConnectionState(true, false, 'excellent');
this.retryManager.reset();
this.startPing();
this.startDataRefresh();
resolve();
};
this.ws.onmessage = (event) => {
this.handleMessage(event.data);
};
this.ws.onclose = (event) => {
console.log('WebSocket closed:', event.code, event.reason);
this.store.updateConnectionState(false, false);
this.stopPing();
this.stopDataRefresh();
if (event.code !== 1000 && this.retryManager.shouldRetry()) {
this.scheduleReconnect();
}
};
this.ws.onerror = (error) => {
console.error('WebSocket error:', error);
this.store.setError('WebSocket连接错误');
this.store.updateConnectionState(false, false);
reject(error);
};
} catch (error) {
this.store.updateConnectionState(false, false);
this.store.setError('无法创建WebSocket连接');
reject(error);
}
});
}
disconnect() {
this.stopPing();
this.stopDataRefresh();
this.retryManager.reset();
// 清理待处理的命令
this.pendingCommands.forEach(({ reject, timeout }) => {
clearTimeout(timeout);
reject(new Error('Connection closed'));
});
this.pendingCommands.clear();
if (this.ws) {
this.ws.close(1000, 'User initiated disconnect');
this.ws = null;
}
this.store.updateConnectionState(false, false, 'disconnected');
}
async sendCommand<T = unknown>(action: WebSocketCommand['action'], data?: unknown): Promise<T> {
if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
throw new Error('WebSocket not connected');
}
const commandId = this.generateId();
const command: WebSocketCommand = { action, data, id: commandId };
const message: WebSocketMessage = {
type: 'command',
data: command,
timestamp: Date.now(),
id: commandId
};
return new Promise((resolve, reject) => {
// 设置超时
const timeout = setTimeout(() => {
this.pendingCommands.delete(commandId);
reject(new Error(`Command timeout: ${action}`));
}, 15000);
// 存储待处理命令
this.pendingCommands.set(commandId, { resolve, reject, timeout });
// 发送命令
try {
this.ws!.send(JSON.stringify(message));
} catch (error) {
this.pendingCommands.delete(commandId);
clearTimeout(timeout);
reject(error);
}
});
}
private handleMessage(data: string) {
try {
const message: WebSocketMessage = JSON.parse(data);
switch (message.type) {
case 'network_status':
this.store.updateNetworkStatus(message.data);
this.emit('network_status', message.data);
break;
case 'connection_info':
this.store.updateConnections(message.data);
this.emit('connection_info', message.data);
break;
case 'firewall_rules':
this.store.updateFirewallRules(message.data);
this.emit('firewall_rules', message.data);
break;
case 'response':
this.handleCommandResponse(message);
break;
case 'error':
this.store.setError(message.data.message || '服务器错误');
this.emit('error', message.data);
break;
default:
this.emit('message', message);
}
} catch (error) {
console.error('Failed to parse WebSocket message:', error);
}
}
private handleCommandResponse(message: WebSocketMessage) {
if (!message.id) return;
const pending = this.pendingCommands.get(message.id);
if (pending) {
clearTimeout(pending.timeout);
this.pendingCommands.delete(message.id);
if (message.data.success) {
pending.resolve(message.data.data);
} else {
pending.reject(new Error(message.data.error || 'Command failed'));
}
}
}
private scheduleReconnect() {
const delay = this.retryManager.getNextDelay();
console.log(`Reconnecting in ${delay}ms (attempt ${this.retryManager.getCurrentAttempt()})`);
setTimeout(() => {
this.connect().catch(console.error);
}, delay);
}
private startPing() {
this.pingTimer = setInterval(() => {
if (this.ws?.readyState === WebSocket.OPEN) {
this.sendPing().catch(() => {
this.store.updateConnectionState(true, false, 'poor');
});
}
}, 30000);
}
private stopPing() {
if (this.pingTimer) {
clearInterval(this.pingTimer);
this.pingTimer = null;
}
}
private startDataRefresh() {
// 定期刷新数据
this.dataRefreshTimer = setInterval(() => {
this.refreshAllData().catch(console.error);
}, 5000); // 每5秒刷新一次
}
private stopDataRefresh() {
if (this.dataRefreshTimer) {
clearInterval(this.dataRefreshTimer);
this.dataRefreshTimer = null;
}
}
private async sendPing() {
const message: WebSocketMessage = {
type: 'command',
data: { action: 'ping' },
timestamp: Date.now()
};
if (this.ws?.readyState === WebSocket.OPEN) {
this.ws.send(JSON.stringify(message));
}
}
private async refreshAllData() {
if (!this.ws || this.ws.readyState !== WebSocket.OPEN) return;
try {
await Promise.all([
this.sendCommand('get_network_status'),
this.sendCommand('get_connections'),
this.sendCommand('get_firewall_rules')
]);
} catch (error) {
console.error('Failed to refresh data:', error);
}
}
private setupEventHandlers() {
// 监听store状态变化
watch(() => this.store.isConnected, (connected) => {
if (connected) {
this.refreshAllData();
}
});
}
// 事件系统
on(event: string, handler: Function) {
if (!this.messageHandlers.has(event)) {
this.messageHandlers.set(event, []);
}
this.messageHandlers.get(event)!.push(handler);
}
off(event: string, handler: Function) {
const handlers = this.messageHandlers.get(event);
if (handlers) {
const index = handlers.indexOf(handler);
if (index > -1) {
handlers.splice(index, 1);
}
}
}
private emit(event: string, data?: unknown) {
const handlers = this.messageHandlers.get(event);
if (handlers) {
handlers.forEach(handler => handler(data));
}
}
private generateId(): string {
return Date.now().toString(36) + Math.random().toString(36).substr(2);
}
// API方法
async getNetworkStatus(): Promise<NetworkStatus[]> {
return this.sendCommand('get_network_status');
}
async getConnections(): Promise<ConnectionInfo[]> {
return this.sendCommand('get_connections');
}
async getFirewallRules(): Promise<FirewallRule[]> {
return this.sendCommand('get_firewall_rules');
}
async addFirewallRule(rule: Omit<FirewallRule, 'id'>): Promise<void> {
return this.sendCommand('add_firewall_rule', rule);
}
async removeFirewallRule(ruleId: string): Promise<void> {
return this.sendCommand('remove_firewall_rule', { id: ruleId });
}
async toggleFirewallRule(ruleId: string): Promise<void> {
return this.sendCommand('toggle_firewall_rule', { id: ruleId });
}
}
// 全局WebSocket管理器实例
let wsManager: EnhancedWebSocketManager | null = null;
export function useWebSocketEnhanced(url?: string) {
if (!wsManager) {
wsManager = new EnhancedWebSocketManager(url);
}
const store = useWebSocketStore();
// 自动连接
if (!store.isConnected && !store.isConnecting) {
wsManager.connect().catch(console.error);
}
onUnmounted(() => {
// 组件卸载时不断开连接,因为是单例
});
return {
// 从store获取状态
...store,
// WebSocketManager方法
connect: () => wsManager!.connect(),
disconnect: () => wsManager!.disconnect(),
sendCommand: (action: WebSocketCommand['action'], data?: unknown) => wsManager!.sendCommand(action, data),
// API方法
getNetworkStatus: () => wsManager!.getNetworkStatus(),
getConnections: () => wsManager!.getConnections(),
getFirewallRules: () => wsManager!.getFirewallRules(),
addFirewallRule: (rule: Omit<FirewallRule, 'id'>) => wsManager!.addFirewallRule(rule),
removeFirewallRule: (ruleId: string) => wsManager!.removeFirewallRule(ruleId),
toggleFirewallRule: (ruleId: string) => wsManager!.toggleFirewallRule(ruleId),
// 事件监听
on: (event: string, handler: Function) => wsManager!.on(event, handler),
off: (event: string, handler: Function) => wsManager!.off(event, handler),
};
}
// plugins/websocket.ts - Vue插件
import type { App } from 'vue';
import { EnhancedWebSocketManager } from '@/composables/useWebSocketEnhanced';
declare module '@vue/runtime-core' {
interface ComponentCustomProperties {
$ws: EnhancedWebSocketManager;
}
}
export default {
install(app: App, options: { url?: string } = {}) {
const wsManager = new EnhancedWebSocketManager(options.url);
app.config.globalProperties.$ws = wsManager;
app.provide('websocket', wsManager);
// 应用关闭时断开连接
window.addEventListener('beforeunload', () => {
wsManager.disconnect();
});
}
};