diff --git a/frontend/hooks/useWebRTCConnection.ts b/frontend/hooks/useWebRTCConnection.ts index aebfc33..4e7a6f2 100644 --- a/frontend/hooks/useWebRTCConnection.ts +++ b/frontend/hooks/useWebRTCConnection.ts @@ -61,7 +61,7 @@ export function useWebRTCConnection({ useEffect(() => { const webRTCConfig = { iceServers: getIceServers(), - socketOptions: getSocketOptions()|| {}, + socketOptions: getSocketOptions() || {}, signalingServer: config.API_URL, }; const senderConn = new WebRTC_Initiator(webRTCConfig); @@ -123,6 +123,12 @@ export function useWebRTCConnection({ }; sender.onDataChannelOpen = () => broadcastDataToAllPeers(shareContent, sendFiles); + + sender.onError = (error) => { + console.error("Sender Error:", error.message, error.context); + // Optionally, use putMessageInMs to show a user-friendly error + putMessageInMs(`Connection error: ${error.message}`, true); + }; } if (receiver && receiverFileTransfer) { @@ -162,6 +168,11 @@ export function useWebRTCConnection({ console.log(`File received from peer ${peerId}: ${file.name}`); onFileReceived(file, peerId || "unknown_peer"); }; + + receiver.onError = (error) => { + console.error("Receiver Error:", error.message, error.context); + putMessageInMs(`Connection error: ${error.message}`, false); + }; } }, [ sender, diff --git a/frontend/lib/webrtc_Initiator.ts b/frontend/lib/webrtc_Initiator.ts index d742531..8a43286 100644 --- a/frontend/lib/webrtc_Initiator.ts +++ b/frontend/lib/webrtc_Initiator.ts @@ -1,100 +1,117 @@ // 发起方 流程: 加入房间; 收到 'ready' 事件(新的接收方进入后socker server就会触发这个事件) -> createPeerConnection + createDataChannel -> createAndSendOffer -import BaseWebRTC, { WebRTCConfig } from './webrtc_base'; -import { postLogInDebug } from '@/app/config/api'; -const developmentEnv = process.env.NEXT_PUBLIC_development!;//开发环境 +import BaseWebRTC, { WebRTCConfig } from "./webrtc_base"; +import { postLogInDebug } from "@/app/config/api"; +const developmentEnv = process.env.NEXT_PUBLIC_development!; //开发环境 export default class WebRTC_Initiator extends BaseWebRTC { constructor(config: WebRTCConfig) { super(config); this.setupInitiatorSocketListeners(); - } private setupInitiatorSocketListeners() { - this.socket.on('ready', ({ peerId }) => {//新进入房间的 接收方 peerId + this.socket.on("ready", ({ peerId }) => { + //新进入房间的 接收方 peerId this.handleReady({ peerId }); }); // 添加接收方响应的监听 - this.socket.on('recipient-ready', ({ peerId }) => { - if (developmentEnv === 'true')postLogInDebug(`[Initiator] Received recipient-ready from: ${peerId}`); + this.socket.on("recipient-ready", ({ peerId }) => { + if (developmentEnv === "true") + postLogInDebug(`[Initiator] Received recipient-ready from: ${peerId}`); this.handleReady({ peerId }); }); // 添加answer处理监听器 - this.socket.on('answer', ({ answer, peerId, from }) => { + this.socket.on("answer", ({ answer, peerId, from }) => { this.handleAnswer({ answer, peerId, from }); }); } //发送方收到接收方加入时创建连接 - private async handleReady({ peerId }: { peerId: string }): Promise {//接收方 peerId - // console.log(`Received ready signal from peer ${peerId}`); - if (developmentEnv === 'true')postLogInDebug(`Received ready signal from peer ${peerId}`); + private async handleReady({ peerId }: { peerId: string }): Promise { + //接收方 peerId + // this.log('log',`Received ready signal from peer ${peerId}`); + if (developmentEnv === "true") + postLogInDebug(`Received ready signal from peer ${peerId}`); await this.createPeerConnection(peerId); await this.createDataChannel(peerId); await this.createAndSendOffer(peerId); } - private async handleAnswer({ answer, peerId, from }: { answer: RTCSessionDescriptionInit; peerId: string; from: string }): Promise { - // console.log(`Handling answer from peer ${from}`); - if (developmentEnv === 'true')postLogInDebug(`Handling answer from peer ${from}`); + private async handleAnswer({ + answer, + peerId, + from, + }: { + answer: RTCSessionDescriptionInit; + peerId: string; + from: string; + }): Promise { + // this.log('log',`Handling answer from peer ${from}`); + if (developmentEnv === "true") + postLogInDebug(`Handling answer from peer ${from}`); const peerConnection = this.peerConnections.get(from); if (!peerConnection) { - console.error(`No peer connection found for peer ${from}`); + this.fireError(`No peer connection found for peer ${from}`, { from }); return; } try { - await peerConnection.setRemoteDescription(new RTCSessionDescription(answer)); - // console.log(`Remote description set for peer ${from}`); - + await peerConnection.setRemoteDescription( + new RTCSessionDescription(answer) + ); + // this.log('log', `Remote description set for peer ${from}`); + // 在设置远程描述后处理队列中的ICE候选 await this.addQueuedIceCandidates(from); } catch (error) { - console.error('Error handling answer:', error); + this.fireError("Error handling answer", { error, from }); } } protected async createDataChannel(peerId: string): Promise { - const peerConnection = this.peerConnections.get(peerId); if (!peerConnection) { - console.error(`No peer connection found for peer ${peerId}`); + this.fireError(`No peer connection found for peer ${peerId}`, { peerId }); return; } try { - const dataChannel = peerConnection.createDataChannel('dataChannel', { + const dataChannel = peerConnection.createDataChannel("dataChannel", { ordered: true, - // reliable: true + // reliable: true }); - // console.log(`Created data channel for peer ${peerId}`); + // this.log('log', `Created data channel for peer ${peerId}`); dataChannel.bufferedAmountLowThreshold = 262144; //256 KB -- 可以根据需要调整 this.setupDataChannel(dataChannel, peerId); this.dataChannels.set(peerId, dataChannel); } catch (error) { - console.error(`Error creating data channel for peer ${peerId}:`, error); + this.fireError(`Error creating data channel for peer ${peerId}`, { + error, + peerId, + }); } } // 如果是发起方,创建并发送offer给信令服务器,以便与接收方协商建立连接。 private async createAndSendOffer(peerId: string): Promise { - // console.log('createAndSendOffer',peerId); - if (developmentEnv === 'true')postLogInDebug(`createAndSendOffer for peerId: ${peerId}`); + // this.log('log', `Creating and sending offer to ${peerId}`); + if (developmentEnv === "true") + postLogInDebug(`createAndSendOffer for peerId: ${peerId}`); const peerConnection = this.peerConnections.get(peerId); if (!peerConnection) { - console.error(`No peer connection found for peer ${peerId}`); + this.fireError(`No peer connection found for peer ${peerId}`, { peerId }); return; } try { const offer = await peerConnection.createOffer(); await peerConnection.setLocalDescription(offer); - // console.log('createAndSendOffer',peerId,this.roomId,offer); - this.socket.emit('offer', { + // this.log('log','createAndSendOffer',peerId,this.roomId,offer); + this.socket.emit("offer", { roomId: this.roomId, peerId: peerId, offer: offer, - from: this.socket.id + from: this.socket.id, }); } catch (error) { - console.error('Error creating and sending offer:', error); + this.fireError("Error creating and sending offer", { error, peerId }); } } -} \ No newline at end of file +} diff --git a/frontend/lib/webrtc_Recipient.ts b/frontend/lib/webrtc_Recipient.ts index 51d06f9..3da325a 100644 --- a/frontend/lib/webrtc_Recipient.ts +++ b/frontend/lib/webrtc_Recipient.ts @@ -1,7 +1,7 @@ // 接收方 流程: 加入房间; 收到 'offer' 事件 -> createPeerConnection + createDataChannel -> 发送 answer -import BaseWebRTC, { WebRTCConfig } from './webrtc_base'; -import { postLogInDebug } from '@/app/config/api'; -const developmentEnv = process.env.NEXT_PUBLIC_development!;//开发环境 +import BaseWebRTC, { WebRTCConfig } from "./webrtc_base"; +import { postLogInDebug } from "@/app/config/api"; +const developmentEnv = process.env.NEXT_PUBLIC_development!; //开发环境 interface AnswerPayload { answer: RTCSessionDescriptionInit; @@ -14,31 +14,41 @@ export default class WebRTC_Recipient extends BaseWebRTC { } private setupRecipientSocketListeners(): void { - - this.socket.on('offer', ({ peerId, offer, from }) => { - this.handleOffer({ peerId,offer, from }); + this.socket.on("offer", ({ peerId, offer, from }) => { + this.handleOffer({ peerId, offer, from }); }); - - this.socket.on('answer', ({ answer, peerId }) => { + + this.socket.on("answer", ({ answer, peerId }) => { this.handleAnswer({ answer, peerId }); }); // 添加发起方重新上线的监听 - this.socket.on('initiator-online', ({ roomId }) => { - console.log(`[Recipient] Received initiator-online for room: ${roomId}`,this.roomId); + this.socket.on("initiator-online", ({ roomId }) => { + this.log("log", `Received initiator-online for room: ${roomId}`); // 发送准备就绪的响应 - console.log(`[Recipient] Sending recipient-ready, my peerId: ${this.socket.id}`,this.peerId); + this.log( + "log", + `Sending recipient-ready, my peerId: ${this.socket.id}`, + this.peerId + ); // 发送准备就绪的响应 - this.socket.emit('recipient-ready', { + this.socket.emit("recipient-ready", { roomId: this.roomId, - peerId: this.socket.id + peerId: this.socket.id, }); }); - } // 接收方 收到 offer 时创建连接 - private async handleOffer({ peerId, offer, from }: { offer: RTCSessionDescriptionInit; peerId: string; from: string }): Promise { - console.log(`Handling offer from peer ${from}`); + private async handleOffer({ + peerId, + offer, + from, + }: { + offer: RTCSessionDescriptionInit; + peerId: string; + from: string; + }): Promise { + this.log("log", `Handling offer from peer ${from}`); try { // 1. 清理已存在的连接 await this.cleanupExistingConnection(from); @@ -49,23 +59,24 @@ export default class WebRTC_Recipient extends BaseWebRTC { // 4. 设置远程描述 // console.log(`Setting remote description for peer ${from}`); - await peerConnection.setRemoteDescription(new RTCSessionDescription(offer)); + await peerConnection.setRemoteDescription( + new RTCSessionDescription(offer) + ); // 创建并设置本地描述(answer) // console.log(`Creating answer for peer ${from}`); const answer = await peerConnection.createAnswer(); await peerConnection.setLocalDescription(answer); // 发送 answer - console.log(`Sending answer to peer ${from}`); - this.socket.emit('answer', { + this.log("log", `Sending answer to peer ${from}`); + this.socket.emit("answer", { answer, peerId: from, - from: this.socket.id + from: this.socket.id, }); // 最后处理已缓存的 ICE candidates await this.addQueuedIceCandidates(from); - } catch (error) { - console.error('Error handling offer:', error); + this.fireError("Error handling offer", { error, from }); // 清理失败的连接 await this.cleanupExistingConnection(from); } @@ -73,26 +84,31 @@ export default class WebRTC_Recipient extends BaseWebRTC { private async handleAnswer({ answer, peerId }: AnswerPayload): Promise { const peerConnection = this.peerConnections.get(peerId); - if (!peerConnection) return; + if (!peerConnection) { + this.fireError("No peer connection for handleAnswer", { peerId }); + return; + } try { - await peerConnection.setRemoteDescription(new RTCSessionDescription(answer)); + await peerConnection.setRemoteDescription( + new RTCSessionDescription(answer) + ); } catch (error) { - console.error('Error handling answer:', error); + this.fireError("Error handling answer", { error, peerId }); } } protected async createDataChannel(peerId: string): Promise { const peerConnection = this.peerConnections.get(peerId); if (!peerConnection) { - console.error(`No peer connection found for peer ${peerId}`); + this.fireError(`No peer connection found for peer ${peerId}`, { peerId }); return; } - + peerConnection.ondatachannel = (event) => { - // console.log(`Received data channel from peer ${peerId}`); + // this.log('log', `Received data channel from peer ${peerId}`); this.setupDataChannel(event.channel, peerId); this.dataChannels.set(peerId, event.channel); }; } -} \ No newline at end of file +} diff --git a/frontend/lib/webrtc_base.ts b/frontend/lib/webrtc_base.ts index 95374ac..45986b5 100644 --- a/frontend/lib/webrtc_base.ts +++ b/frontend/lib/webrtc_base.ts @@ -1,9 +1,16 @@ // BaseWebRTC.js -import io, { Socket, ManagerOptions, SocketOptions } from 'socket.io-client'; -import { getIceServers, getSocketOptions } from '@/app/config/environment'; -import { WakeLockManager } from './wakeLockManager'; -import { postLogInDebug } from '@/app/config/api'; -const developmentEnv = process.env.NEXT_PUBLIC_development!;//开发环境 +import io, { Socket, ManagerOptions, SocketOptions } from "socket.io-client"; +import { getIceServers, getSocketOptions } from "@/app/config/environment"; +import { WakeLockManager } from "./wakeLockManager"; +import { postLogInDebug } from "@/app/config/api"; +const developmentEnv = process.env.NEXT_PUBLIC_development!; //开发环境 + +export class WebRTCError extends Error { + constructor(message: string, public context?: Record) { + super(message); + this.name = "WebRTCError"; + } +} interface JoinRoomResponse { success: boolean; @@ -11,17 +18,22 @@ interface JoinRoomResponse { roomId: string; error?: string; } + interface CallbackTypes { onDataChannelOpen?: (peerId: string) => void; onDataReceived?: (data: string | ArrayBuffer, peerId: string) => void; onConnectionEstablished?: (peerId: string) => void; - onConnectionStateChange?: (state: RTCPeerConnectionState, peerId: string) => void; + onConnectionStateChange?: ( + state: RTCPeerConnectionState, + peerId: string + ) => void; + onError?: (error: WebRTCError) => void; } export interface WebRTCConfig { iceServers: RTCIceServer[]; socketOptions: Partial; - signalingServer: string;// signalingServer: 信令服务器的URL,用于初始化Socket.IO连接。 + signalingServer: string; // signalingServer: 信令服务器的URL,用于初始化Socket.IO连接。 } export default class BaseWebRTC { @@ -31,38 +43,44 @@ export default class BaseWebRTC { public peerConnections: Map; public dataChannels: Map; - public onDataChannelOpen: CallbackTypes['onDataChannelOpen'] | null; - public onDataReceived: CallbackTypes['onDataReceived'] | null; - protected onConnectionEstablished: CallbackTypes['onConnectionEstablished'] | null; - public onConnectionStateChange: CallbackTypes['onConnectionStateChange'] | null; + public onDataChannelOpen: CallbackTypes["onDataChannelOpen"] | null; + public onDataReceived: CallbackTypes["onDataReceived"] | null; + protected onConnectionEstablished: + | CallbackTypes["onConnectionEstablished"] + | null; + public onConnectionStateChange: + | CallbackTypes["onConnectionStateChange"] + | null; + public onError: CallbackTypes["onError"] | null; protected iceCandidatesQueue: Map; protected roomId: string | null; protected peerId: string | undefined | null; public isInRoom: boolean; - protected isInitiator: boolean;//标记发起方 + protected isInitiator: boolean; //标记发起方 //重连相关 - protected isSocketDisconnected: boolean;//跟踪 socket 连接状态 - protected isPeerDisconnected: boolean;//跟踪 P2P 连接状态 - protected reconnectionInProgress: boolean;//防止重复重连 + protected isSocketDisconnected: boolean; //跟踪 socket 连接状态 + protected isPeerDisconnected: boolean; //跟踪 P2P 连接状态 + protected reconnectionInProgress: boolean; //防止重复重连 protected wakeLockManager: WakeLockManager; constructor(config: WebRTCConfig) { this.iceServers = config.iceServers; this.socket = io(config.signalingServer, config.socketOptions); - this.peerConnections = new Map();// Map - this.dataChannels = new Map();// Map - + this.peerConnections = new Map(); // Map + this.dataChannels = new Map(); // Map + // Callbacks - this.onDataChannelOpen = null;//当数据通道建立时的回调 - this.onDataReceived = null;//接收数据--响应 - this.onConnectionEstablished = null;//当WebRTC连接建立时触发。 - this.onConnectionStateChange = null;//监控和响应连接状态的变化 - - this.iceCandidatesQueue = new Map();// 为每个peer存储ice候选项 + this.onDataChannelOpen = null; //当数据通道建立时的回调 + this.onDataReceived = null; //接收数据--响应 + this.onConnectionEstablished = null; //当WebRTC连接建立时触发。 + this.onConnectionStateChange = null; //监控和响应连接状态的变化 + this.onError = null; + + this.iceCandidatesQueue = new Map(); // 为每个peer存储ice候选项 this.roomId = null; - this.peerId = null;//自己的 ID - this.isInRoom = false;//是否已经加入过房间 + this.peerId = null; //自己的 ID + this.isInRoom = false; //是否已经加入过房间 this.setupCommonSocketListeners(); this.isInitiator = false; @@ -72,61 +90,90 @@ export default class BaseWebRTC { this.reconnectionInProgress = false; this.wakeLockManager = new WakeLockManager(); } + // region Logging and Error Handling + protected log( + level: "log" | "warn" | "error", + message: string, + ...args: any[] + ) { + const prefix = `[${this.constructor.name}]`; + console[level](prefix, message, ...args); + } + + protected fireError(message: string, context?: Record) { + const error = new WebRTCError(message, context); + this.log("error", message, context); + this.onError?.(error); + } + // endregion // 设置信令服务器的事件监听器,用于处理各种信令消息(连接、ICE候选者、offer、answer等)。 setupCommonSocketListeners() { - this.socket.on('connect', () => { - this.peerId = this.socket.id;//保存自己的 ID - console.log('Connected to signaling server, peerId:', this.peerId); + this.socket.on("connect", () => { + this.peerId = this.socket.id; //保存自己的 ID + this.log("log", `Connected to signaling server, peerId: ${this.peerId}`); }); - this.socket.on('error', (error) => { - console.error('Socket error:', error); + this.socket.on("error", (error) => { + this.fireError("Socket error", { error }); }); - this.socket.on('ice-candidate', ({ candidate, peerId, from }) => {//接受方 peerId + this.socket.on("ice-candidate", ({ candidate, peerId, from }) => { + //接受方 peerId // console.log(`Received ICE candidate from ${from} for ${peerId}`); - this.handleIceCandidate({candidate, peerId, from}); + this.handleIceCandidate({ candidate, peerId, from }); }); // 添加 socket 断开连接的监听 - this.socket.on('disconnect', () => { + this.socket.on("disconnect", () => { this.isInRoom = false; this.isSocketDisconnected = true; - if(developmentEnv === 'true')postLogInDebug(`${this.peerId} disconnect on socket,isInitiator:${this.isInitiator},isInRoom:${this.isInRoom}`); + if (developmentEnv === "true") + postLogInDebug( + `${this.peerId} disconnect on socket,isInitiator:${this.isInitiator},isInRoom:${this.isInRoom}` + ); // 尝试重连.//移动端切换到后台之后,P2P连接和socket连接都会断开.在切回来时,才会执行断开的代码,直接在这里重连;发送重连开始新号 this.attemptReconnection(); }); } protected async attemptReconnection(): Promise { if (this.reconnectionInProgress) return; - - if (this.isSocketDisconnected && this.isPeerDisconnected && this.roomId) {//等socket和P2P连接都断开之后再开始重连 + + if (this.isSocketDisconnected && this.isPeerDisconnected && this.roomId) { + //等socket和P2P连接都断开之后再开始重连 this.reconnectionInProgress = true; - if(developmentEnv === 'true') { - postLogInDebug(`Starting reconnection, socket and peer both disconnected. isInitiator:${this.isInitiator}`); + if (developmentEnv === "true") { + postLogInDebug( + `Starting reconnection, socket and peer both disconnected. isInitiator:${this.isInitiator}` + ); } - + try { const sendInitiatorOnline = this.isInitiator; await this.joinRoom(this.roomId, this.isInitiator, sendInitiatorOnline); - + // 重置状态 this.isSocketDisconnected = false; this.isPeerDisconnected = false; } catch (error) { - console.error('Reconnection failed:', error); + this.fireError("Reconnection failed", { error }); } finally { this.reconnectionInProgress = false; } } } - protected async handleIceCandidate( - { candidate, peerId, from }: { candidate: RTCIceCandidateInit; peerId: string; from: string } - ): Promise { - // console.log(`Handling ICE candidate from ${from} for ${peerId}`); + protected async handleIceCandidate({ + candidate, + peerId, + from, + }: { + candidate: RTCIceCandidateInit; + peerId: string; + from: string; + }): Promise { + // this.log('log',`Handling ICE candidate from ${from} for ${peerId}`); const peerConnection = this.peerConnections.get(from); - // console.log(`this.peerConnections`,this.peerConnections); + // this.log('log',`this.peerConnections`,this.peerConnections); if (!peerConnection) { - // console.warn(`No peer connection found for ${from}, queuing candidate`); + // this.log('warn',`No peer connection found for ${from}, queuing candidate`); if (!this.iceCandidatesQueue.has(from)) { this.iceCandidatesQueue.set(from, []); } @@ -135,21 +182,23 @@ export default class BaseWebRTC { } try { // 只有在远程描述设置完成且连接未关闭的情况下才添加ICE候选项 - if (peerConnection.remoteDescription && - peerConnection.signalingState !== 'closed' && - peerConnection.connectionState !== 'closed') { + if ( + peerConnection.remoteDescription && + peerConnection.signalingState !== "closed" && + peerConnection.connectionState !== "closed" + ) { await peerConnection.addIceCandidate(new RTCIceCandidate(candidate)); - // console.log(`Successfully added ICE candidate for ${from}`); + // this.log('log',`Successfully added ICE candidate for ${from}`); } else { - // console.warn(`Remote description not set or connection closed for ${from}, queuing candidate`); - // console.warn(`remoteDescription`,peerConnection.remoteDescription,'peerConnection.signalingState',peerConnection.signalingState); + // this.log('warn',`Remote description not set or connection closed for ${from}, queuing candidate`); + // this.log('warn',`remoteDescription`,peerConnection.remoteDescription,'peerConnection.signalingState',peerConnection.signalingState); if (!this.iceCandidatesQueue.has(from)) { - this.iceCandidatesQueue.set(from, []); + this.iceCandidatesQueue.set(from, []); } this.iceCandidatesQueue.get(from)?.push(candidate); } } catch (e) { - console.error(`Error adding ICE candidate for ${from}:`, e); + this.fireError(`Error adding ICE candidate for ${from}`, { error: e }); // 如果添加失败,也将其加入队列 if (!this.iceCandidatesQueue.has(from)) { this.iceCandidatesQueue.set(from, []); @@ -162,88 +211,100 @@ export default class BaseWebRTC { const candidates = this.iceCandidatesQueue.get(peerId); const peerConnection = this.peerConnections.get(peerId); - // console.log(`Attempting to add ${candidates?.length || 0} queued candidates for ${peerId}`); - // console.log(`Connection state: ${peerConnection?.connectionState}`); - // console.log(`Signaling state: ${peerConnection?.signalingState}`); + // this.log('log',`Attempting to add ${candidates?.length || 0} queued candidates for ${peerId}`); + // this.log('log',`Connection state: ${peerConnection?.connectionState}`); + // this.log('log',`Signaling state: ${peerConnection?.signalingState}`); if (!peerConnection || !candidates?.length) { return; } - if (peerConnection.remoteDescription && - peerConnection.signalingState !== 'closed' && - peerConnection.connectionState !== 'closed') { - - // console.log(`Adding ${candidates.length} queued candidates for ${peerId}`); + if ( + peerConnection.remoteDescription && + peerConnection.signalingState !== "closed" && + peerConnection.connectionState !== "closed" + ) { + // this.log('log',`Adding ${candidates.length} queued candidates for ${peerId}`); for (const candidate of candidates) { try { await peerConnection.addIceCandidate(new RTCIceCandidate(candidate)); - // console.log(`Successfully added queued candidate for ${peerId}`); - } - catch (e) { - console.error('Error adding queued ice candidates', e); + // this.log('log',`Successfully added queued candidate for ${peerId}`); + } catch (e) { + this.fireError("Error adding queued ice candidates", { + error: e, + peerId, + }); } } // 只有在成功添加所有候选项后才清空队列 this.iceCandidatesQueue.delete(peerId); - } else { - console.warn(`Connection not ready for ${peerId}, keeping candidates queued`); - // console.warn(`remoteDescription`,peerConnection?.remoteDescription); + this.log( + "warn", + `Connection not ready for ${peerId}, keeping candidates queued` + ); + // this.log('warn',`remoteDescription`,peerConnection?.remoteDescription); } } - protected async createPeerConnection(peerId: string): Promise { - // console.log('Creating peer connection for:', peerId); + protected async createPeerConnection( + peerId: string + ): Promise { + // this.log('log','Creating peer connection for:', peerId); const peerConnection = this.peerConnections.get(peerId); if (peerConnection) { - // console.log('Reusing existing peer connection for:', peerId); + // this.log('log','Reusing existing peer connection for:', peerId); return Promise.resolve(peerConnection); } // WebRTC默认提供了强大的加密功能,上线后要改为https协议 - const newPeerConnection = new RTCPeerConnection({ iceServers: this.iceServers }); + const newPeerConnection = new RTCPeerConnection({ + iceServers: this.iceServers, + }); // // 增加更详细的连接状态监控 // newPeerConnection.oniceconnectionstatechange = () => { - // console.log(`ICE Connection State (${peerId}):`, newPeerConnection.iceConnectionState); + // this.log('log',`ICE Connection State (${peerId}):`, newPeerConnection.iceConnectionState); // }; // newPeerConnection.onsignalingstatechange = () => { - // console.log(`Signaling State (${peerId}):`, newPeerConnection.signalingState); + // this.log('log',`Signaling State (${peerId}):`, newPeerConnection.signalingState); // }; newPeerConnection.onconnectionstatechange = () => { // const state = newPeerConnection.connectionState; - // console.log(`Connection State (${peerId}):`, state); + // this.log('log',`Connection State (${peerId}):`, state); this.handleConnectionStateChange(peerId, newPeerConnection); }; // 改进ICE候选项处理 newPeerConnection.onicecandidate = (event) => { if (event.candidate) { - // console.log(`Sending ICE candidate to ${peerId}:`, event.candidate); - this.socket.emit('ice-candidate', { + // this.log('log',`Sending ICE candidate to ${peerId}:`, event.candidate); + this.socket.emit("ice-candidate", { candidate: event.candidate, peerId: peerId, - from: this.socket.id // 添加发送方ID + from: this.socket.id, // 添加发送方ID }); } }; // // 添加ICE收集状态监控 // newPeerConnection.onicegatheringstatechange = () => { - // console.log(`ICE Gathering State (${peerId}):`, newPeerConnection.iceGatheringState); + // this.log('log',`ICE Gathering State (${peerId}):`, newPeerConnection.iceGatheringState); // }; this.peerConnections.set(peerId, newPeerConnection); - // console.log('New peer connection created for:', peerId); + // this.log('log','New peer connection created for:', peerId); return Promise.resolve(newPeerConnection); } - protected handleConnectionStateChange(peerId: string, peerConnection: RTCPeerConnection): void { + protected handleConnectionStateChange( + peerId: string, + peerConnection: RTCPeerConnection + ): void { const state = peerConnection.connectionState; - // console.log('Connection state change:', state); - + // this.log('log','Connection state change:', state); + const stateHandlers = { connected: async () => { this.isPeerDisconnected = false; @@ -258,33 +319,41 @@ export default class BaseWebRTC { disconnected: async () => { await this.cleanupExistingConnection(peerId); this.isPeerDisconnected = true; - if (developmentEnv === 'true')postLogInDebug(`p2p disconnected, isInitiator:${this.isInitiator}`); + if (developmentEnv === "true") + postLogInDebug(`p2p disconnected, isInitiator:${this.isInitiator}`); // 尝试重连 this.attemptReconnection(); await this.wakeLockManager.releaseWakeLock(); }, - failed: async () => { + failed: async () => { this.cleanupExistingConnection(peerId); this.isPeerDisconnected = true; await this.wakeLockManager.releaseWakeLock(); }, - closed: async () => { + closed: async () => { this.cleanupExistingConnection(peerId); this.isPeerDisconnected = true; await this.wakeLockManager.releaseWakeLock(); }, // 以下必须添加,防止报错 - connecting: () => {console.log("Peer is connecting");}, - new: () => {console.log("New connection state");} + connecting: () => { + this.log("log", "Peer is connecting"); + }, + new: () => { + this.log("log", "New connection state"); + }, }; stateHandlers[state]?.(); this.onConnectionStateChange?.(state, peerId); } - - protected setupDataChannel(dataChannel: RTCDataChannel, peerId: string): void { + + protected setupDataChannel( + dataChannel: RTCDataChannel, + peerId: string + ): void { dataChannel.onopen = () => { - // console.log(`Data channel opened for peer ${peerId}`); + // this.log('log',`Data channel opened for peer ${peerId}`); setTimeout(() => { this.onDataChannelOpen?.(peerId); }, 50); @@ -295,7 +364,11 @@ export default class BaseWebRTC { }; } // 加入房间,sendInitiatorOnline表示加入房间之后,是否要发送“发起方重新在线”消息 - public async joinRoom(roomId: string, isInitiator:boolean, sendInitiatorOnline:boolean = false): Promise { + public async joinRoom( + roomId: string, + isInitiator: boolean, + sendInitiatorOnline: boolean = false + ): Promise { // 如果已经在房间里,直接返回 if (this.isInRoom) { return; @@ -304,38 +377,42 @@ export default class BaseWebRTC { return new Promise((resolve, reject) => { // 设置超时时间(5秒) const timeout = setTimeout(() => { - this.socket.off('joinResponse'); - reject(new Error('Join room timeout')); + this.socket.off("joinResponse"); + reject(new Error("Join room timeout")); this.isInRoom = false; this.roomId = null; }, 5000); - + // 监听加入房间响应--一次 - this.socket.once('joinResponse', (response: JoinRoomResponse) => { + this.socket.once("joinResponse", (response: JoinRoomResponse) => { clearTimeout(timeout); // 清除超时定时器 - + if (response.success) { this.roomId = roomId; this.isInRoom = true; - if(sendInitiatorOnline){ - this.socket.emit('initiator-online', { - roomId: this.roomId + if (sendInitiatorOnline) { + this.socket.emit("initiator-online", { + roomId: this.roomId, }); } - if (developmentEnv === 'true')postLogInDebug(`peerId:${this.socket.id} Successfully joined room: ${response.roomId},isInitiator:${this.isInitiator},isInRoom:${this.isInRoom}`); + if (developmentEnv === "true") + postLogInDebug( + `peerId:${this.socket.id} Successfully joined room: ${response.roomId},isInitiator:${this.isInitiator},isInRoom:${this.isInRoom}` + ); resolve(); } else { this.isInRoom = false; this.roomId = null; - if (developmentEnv === 'true')postLogInDebug(`Failed to join room,message:${response.message}`); - console.error('Failed to join room:', response.message); + if (developmentEnv === "true") + postLogInDebug(`Failed to join room,message:${response.message}`); + this.fireError("Failed to join room", { message: response.message }); reject(new Error(response.message)); } }); - + // 发送加入房间请求 try { - this.socket.emit('join', {roomId}); + this.socket.emit("join", { roomId }); } catch (error) { clearTimeout(timeout); this.isInRoom = false; @@ -361,12 +438,12 @@ export default class BaseWebRTC { //发送给特定对象 protected sendToPeer(data: any, peerId: string): boolean { const dataChannel = this.dataChannels.get(peerId); - if (dataChannel?.readyState === 'open') { + if (dataChannel?.readyState === "open") { dataChannel.send(data); return true; } - - console.warn(`Data channel not ready for peer ${peerId}. Retrying...`); + + this.log("warn", `Data channel not ready for peer ${peerId}. Retrying...`); this.retryDataSend(data, peerId); return false; } @@ -377,14 +454,19 @@ export default class BaseWebRTC { const attemptSend = () => { const dataChannel = this.dataChannels.get(peerId); - if (dataChannel?.readyState === 'open') { + if (dataChannel?.readyState === "open") { dataChannel.send(data); } else if (retryCount < maxRetries) { retryCount++; - console.log(`Retrying to send data to peer ${peerId}. Attempt ${retryCount} of ${maxRetries}`); + this.log( + "log", + `Retrying to send data to peer ${peerId}. Attempt ${retryCount} of ${maxRetries}` + ); setTimeout(attemptSend, 1000); } else { - console.error(`Failed to send data to peer ${peerId} after maximum retries`); + this.fireError( + `Failed to send data to peer ${peerId} after maximum retries` + ); } }; @@ -422,6 +504,6 @@ export default class BaseWebRTC { } // 抽象方法声明 protected createDataChannel(peerId: string) { - throw new Error('createDataChannel must be implemented by subclass'); + throw new Error("createDataChannel must be implemented by subclass"); } -} \ No newline at end of file +}