2026-03-12 12:10:40 +08:00
|
|
|
|
// /**
|
|
|
|
|
|
// * WebSocket管理
|
|
|
|
|
|
// * 管理WebSocket连接,处理WebSocket事件
|
|
|
|
|
|
// */
|
|
|
|
|
|
// import { RenderStreaming } from "../../module/renderstreaming.js"; // WebRTC连接管理
|
|
|
|
|
|
|
|
|
|
|
|
// class WebSocketManager {
|
|
|
|
|
|
// constructor() {
|
|
|
|
|
|
// this.isConnected = false;
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
// /**
|
|
|
|
|
|
// * 获取默认WebSocket URL
|
|
|
|
|
|
// * @returns {string} WebSocket URL
|
|
|
|
|
|
// */
|
|
|
|
|
|
// getDefaultWebSocketUrl() {
|
|
|
|
|
|
// const protocol = location.protocol === 'https:' ? 'wss:' : 'ws:';
|
|
|
|
|
|
// return `${protocol}//${location.host}`;
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
// /**
|
|
|
|
|
|
// * 连接WebSocket
|
|
|
|
|
|
// */
|
|
|
|
|
|
// init() {
|
|
|
|
|
|
// try {
|
|
|
|
|
|
|
|
|
|
|
|
// RenderStreaming.onConnect = () => {
|
|
|
|
|
|
// console.log('WebSocket connected');
|
|
|
|
|
|
// this.isConnected = true;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// // // 发送连接消息
|
|
|
|
|
|
// // this.sendConnectMessage();
|
|
|
|
|
|
|
|
|
|
|
|
// // // 启动心跳
|
|
|
|
|
|
// this.startHeartbeat();
|
|
|
|
|
|
|
|
|
|
|
|
// // this.emit('connect');
|
|
|
|
|
|
// };
|
|
|
|
|
|
|
|
|
|
|
|
// RenderStreaming.onDisconnect = () => {
|
|
|
|
|
|
// console.log('WebSocket disconnected');
|
|
|
|
|
|
// this.isConnected = false;
|
|
|
|
|
|
// this.stopHeartbeat();
|
|
|
|
|
|
// //this.emit('disconnect');
|
|
|
|
|
|
// //this.attemptReconnect();
|
|
|
|
|
|
// };
|
|
|
|
|
|
|
|
|
|
|
|
// RenderStreaming.onmessage = (event) => {
|
|
|
|
|
|
// try {
|
|
|
|
|
|
// const message = JSON.parse(event.data);
|
|
|
|
|
|
// this.handleMessage(message);
|
|
|
|
|
|
// } catch (error) {
|
|
|
|
|
|
// console.error('Error parsing WebSocket message:', error);
|
|
|
|
|
|
// }
|
|
|
|
|
|
// };
|
|
|
|
|
|
|
|
|
|
|
|
// this.socket.onerror = (error) => {
|
|
|
|
|
|
// console.error('WebSocket error:', error);
|
|
|
|
|
|
// this.emit('error', error);
|
|
|
|
|
|
// };
|
|
|
|
|
|
// } catch (error) {
|
|
|
|
|
|
// console.error('Error connecting to WebSocket:', error);
|
|
|
|
|
|
// this.emit('error', error);
|
|
|
|
|
|
// }
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
// /**
|
|
|
|
|
|
// * 断开WebSocket连接
|
|
|
|
|
|
// */
|
|
|
|
|
|
// disconnect() {
|
|
|
|
|
|
// if (this.socket) {
|
|
|
|
|
|
// this.socket.close();
|
|
|
|
|
|
// this.socket = null;
|
|
|
|
|
|
// this.isConnected = false;
|
|
|
|
|
|
// }
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
// /**
|
|
|
|
|
|
// * 发送消息
|
|
|
|
|
|
// * @param {string} type - 消息类型
|
|
|
|
|
|
// * @param {Object} data - 消息数据
|
|
|
|
|
|
// */
|
|
|
|
|
|
// send(type, data) {
|
|
|
|
|
|
// if (this.isConnected && this.socket) {
|
|
|
|
|
|
// try {
|
|
|
|
|
|
// let message;
|
|
|
|
|
|
|
|
|
|
|
|
// // 根据消息类型构建不同的消息格式
|
|
|
|
|
|
// if (type === 'connect' || type === 'disconnect') {
|
|
|
|
|
|
// message = JSON.stringify({ type, connectionId: this.connectionId });
|
|
|
|
|
|
// } else if (type === 'offer' || type === 'answer' || type === 'candidate') {
|
|
|
|
|
|
// message = JSON.stringify({ type, data });
|
|
|
|
|
|
// } else if (type === 'broadcast') {
|
|
|
|
|
|
// message = JSON.stringify({ type, message: data.message, targetConnectionId: data.targetConnectionId });
|
|
|
|
|
|
// } else if (type === 'ping' || type === 'pong') {
|
|
|
|
|
|
// message = JSON.stringify({ type });
|
|
|
|
|
|
// } else {
|
|
|
|
|
|
// // 兼容旧格式,用于自定义事件
|
|
|
|
|
|
// message = JSON.stringify({ event: type, data });
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
// this.socket.send(message);
|
|
|
|
|
|
// } catch (error) {
|
|
|
|
|
|
// console.error('Error sending WebSocket message:', error);
|
|
|
|
|
|
// }
|
|
|
|
|
|
// } else {
|
|
|
|
|
|
// console.warn('WebSocket not connected, cannot send message');
|
|
|
|
|
|
// }
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
// /**
|
|
|
|
|
|
// * 处理接收到的消息
|
|
|
|
|
|
// * @param {Object} message - 消息对象
|
|
|
|
|
|
// */
|
|
|
|
|
|
// handleMessage(message) {
|
|
|
|
|
|
// if (message.type) {
|
|
|
|
|
|
// console.log('Received message:', message);
|
|
|
|
|
|
// switch (message.type) {
|
|
|
|
|
|
// case 'user-joined':
|
|
|
|
|
|
// this.emit('user-joined', message.data);
|
|
|
|
|
|
// break;
|
|
|
|
|
|
// case 'user-left':
|
|
|
|
|
|
// this.emit('user-left', message.data);
|
|
|
|
|
|
// break;
|
|
|
|
|
|
// case 'media-state-changed':
|
|
|
|
|
|
// this.emit('media-state-changed', message.data);
|
|
|
|
|
|
// break;
|
|
|
|
|
|
// case 'message-received':
|
|
|
|
|
|
// this.emit('message-received', message.data);
|
|
|
|
|
|
// break;
|
|
|
|
|
|
// case 'network-quality':
|
|
|
|
|
|
// this.emit('network-quality', message.data);
|
|
|
|
|
|
// break;
|
|
|
|
|
|
// case 'call-ended':
|
|
|
|
|
|
// this.emit('call-ended', message.data);
|
|
|
|
|
|
// break;
|
|
|
|
|
|
// case 'call-request':
|
|
|
|
|
|
// this.emit('call-request', message.data);
|
|
|
|
|
|
// break;
|
|
|
|
|
|
// case 'ping':
|
|
|
|
|
|
// // 处理心跳请求,回复pong
|
|
|
|
|
|
// this.send('pong');
|
|
|
|
|
|
// break;
|
|
|
|
|
|
// case 'pong':
|
|
|
|
|
|
// // 处理心跳响应
|
|
|
|
|
|
// this.emit('pong');
|
|
|
|
|
|
// break;
|
|
|
|
|
|
// case 'offer':
|
|
|
|
|
|
// this.emit('offer', message.data);
|
|
|
|
|
|
// break;
|
|
|
|
|
|
// case 'answer':
|
|
|
|
|
|
// this.emit('answer', message.data);
|
|
|
|
|
|
// break;
|
|
|
|
|
|
// case 'candidate':
|
|
|
|
|
|
// this.emit('candidate', message.data);
|
|
|
|
|
|
// break;
|
|
|
|
|
|
// case 'chat-message':
|
|
|
|
|
|
// this.emit('chat-message', message.data);
|
|
|
|
|
|
// break;
|
|
|
|
|
|
// default:
|
|
|
|
|
|
// // 处理旧格式消息
|
|
|
|
|
|
// if (message.event) {
|
|
|
|
|
|
// this.emit(message.event, message.data);
|
|
|
|
|
|
// } else {
|
|
|
|
|
|
// this.emit('message', message);
|
|
|
|
|
|
// }
|
|
|
|
|
|
// break;
|
|
|
|
|
|
// }
|
|
|
|
|
|
// } else if (message.event) {
|
|
|
|
|
|
// // 处理旧格式消息
|
|
|
|
|
|
// this.emit(message.event, message.data);
|
|
|
|
|
|
// } else {
|
|
|
|
|
|
// this.emit('message', message);
|
|
|
|
|
|
// }
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
// /**
|
|
|
|
|
|
// * 尝试重连
|
|
|
|
|
|
// */
|
|
|
|
|
|
// attemptReconnect() {
|
|
|
|
|
|
// if (this.reconnectAttempts < this.maxReconnectAttempts) {
|
|
|
|
|
|
// this.reconnectAttempts++;
|
|
|
|
|
|
// const delay = this.reconnectDelay * Math.pow(2, this.reconnectAttempts - 1);
|
|
|
|
|
|
|
|
|
|
|
|
// console.log(`Attempting to reconnect in ${delay}ms...`);
|
|
|
|
|
|
|
|
|
|
|
|
// setTimeout(() => {
|
|
|
|
|
|
// console.log(`Reconnect attempt ${this.reconnectAttempts}/${this.maxReconnectAttempts}`);
|
|
|
|
|
|
// this.connect();
|
|
|
|
|
|
// }, delay);
|
|
|
|
|
|
// } else {
|
|
|
|
|
|
// console.error('Max reconnect attempts reached');
|
|
|
|
|
|
// this.emit('reconnect-failed');
|
|
|
|
|
|
// }
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
// /**
|
|
|
|
|
|
// * 生成连接ID
|
|
|
|
|
|
// * @returns {string} 连接ID
|
|
|
|
|
|
// */
|
|
|
|
|
|
// generateConnectionId() {
|
|
|
|
|
|
// return 'conn_' + Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15);
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
// /**
|
|
|
|
|
|
// * 发送连接消息
|
|
|
|
|
|
// */
|
|
|
|
|
|
// sendConnectMessage() {
|
|
|
|
|
|
// this.send('connect');
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
// /**
|
|
|
|
|
|
// * 发送断开连接消息
|
|
|
|
|
|
// */
|
|
|
|
|
|
// sendDisconnectMessage() {
|
|
|
|
|
|
// this.send('disconnect');
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
// /**
|
|
|
|
|
|
// * 启动心跳
|
|
|
|
|
|
// */
|
|
|
|
|
|
// startHeartbeat() {
|
|
|
|
|
|
// this.heartbeatInterval = setInterval(() => {
|
|
|
|
|
|
// if (this.isConnected) {
|
|
|
|
|
|
// this.send('ping');
|
|
|
|
|
|
// }
|
|
|
|
|
|
// }, 30000); // 每30秒发送一次心跳
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
// /**
|
|
|
|
|
|
// * 停止心跳
|
|
|
|
|
|
// */
|
|
|
|
|
|
// stopHeartbeat() {
|
|
|
|
|
|
// if (this.heartbeatInterval) {
|
|
|
|
|
|
// clearInterval(this.heartbeatInterval);
|
|
|
|
|
|
// this.heartbeatInterval = null;
|
|
|
|
|
|
// }
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
// /**
|
|
|
|
|
|
// * 获取连接ID
|
|
|
|
|
|
// * @returns {string} 连接ID
|
|
|
|
|
|
// */
|
|
|
|
|
|
// getConnectionId() {
|
|
|
|
|
|
// return this.connectionId;
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
// /**
|
|
|
|
|
|
// * 订阅事件
|
|
|
|
|
|
// * @param {string} event - 事件名称
|
|
|
|
|
|
// * @param {Function} callback - 回调函数
|
|
|
|
|
|
// */
|
|
|
|
|
|
// on(event, callback) {
|
|
|
|
|
|
// if (!this.listeners.has(event)) {
|
|
|
|
|
|
// this.listeners.set(event, []);
|
|
|
|
|
|
// }
|
|
|
|
|
|
// this.listeners.get(event).push(callback);
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
// /**
|
|
|
|
|
|
// * 取消订阅事件
|
|
|
|
|
|
// * @param {string} event - 事件名称
|
|
|
|
|
|
// * @param {Function} callback - 回调函数
|
|
|
|
|
|
// */
|
|
|
|
|
|
// off(event, callback) {
|
|
|
|
|
|
// if (this.listeners.has(event)) {
|
|
|
|
|
|
// const callbacks = this.listeners.get(event);
|
|
|
|
|
|
// this.listeners.set(event, callbacks.filter(cb => cb !== callback));
|
|
|
|
|
|
// }
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
// /**
|
|
|
|
|
|
// * 触发事件
|
|
|
|
|
|
// * @param {string} event - 事件名称
|
|
|
|
|
|
// * @param {*} data - 事件数据
|
|
|
|
|
|
// */
|
|
|
|
|
|
// emit(event, data) {
|
|
|
|
|
|
// if (this.listeners.has(event)) {
|
|
|
|
|
|
// this.listeners.get(event).forEach(callback => {
|
|
|
|
|
|
// try {
|
|
|
|
|
|
// callback(data);
|
|
|
|
|
|
// } catch (error) {
|
|
|
|
|
|
// console.error(`Error in event listener for ${event}:`, error);
|
|
|
|
|
|
// }
|
|
|
|
|
|
// });
|
|
|
|
|
|
// }
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
// /**
|
|
|
|
|
|
// * 检查连接状态
|
|
|
|
|
|
// * @returns {boolean} 是否连接
|
|
|
|
|
|
// */
|
|
|
|
|
|
// getIsConnected() {
|
|
|
|
|
|
// return this.isConnected;
|
|
|
|
|
|
// }
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
// // 创建单例实例
|
|
|
|
|
|
// const wsManager = new WebSocketManager();
|
|
|
|
|
|
|
|
|
|
|
|
// export default wsManager;
|