diff --git a/chuan-next/src/components/ConnectionStatus.tsx b/chuan-next/src/components/ConnectionStatus.tsx
index bf3e72d..5839e0d 100644
--- a/chuan-next/src/components/ConnectionStatus.tsx
+++ b/chuan-next/src/components/ConnectionStatus.tsx
@@ -14,11 +14,12 @@ interface ConnectionStatusProps {
}
// 连接状态枚举
-const getConnectionStatus = (connection: { isWebSocketConnected?: boolean; isPeerConnected?: boolean; isConnecting?: boolean; error?: string | null }, currentRoom: { code: string; role: 'sender' | 'receiver' } | null) => {
+const getConnectionStatus = (connection: { isWebSocketConnected?: boolean; isPeerConnected?: boolean; isConnecting?: boolean; error?: string | null; transportMode?: string }, currentRoom: { code: string; role: 'sender' | 'receiver' } | null) => {
const isWebSocketConnected = connection?.isWebSocketConnected || false;
const isPeerConnected = connection?.isPeerConnected || false;
const isConnecting = connection?.isConnecting || false;
const error = connection?.error || null;
+ const transportMode = connection?.transportMode || 'p2p';
if (error) {
return {
@@ -32,7 +33,7 @@ const getConnectionStatus = (connection: { isWebSocketConnected?: boolean; isPee
return {
type: 'connecting' as const,
message: '正在连接',
- detail: '建立房间连接中...',
+ detail: transportMode === 'relay' ? '正在建立中继连接...' : '建立房间连接中...',
};
}
@@ -45,7 +46,6 @@ const getConnectionStatus = (connection: { isWebSocketConnected?: boolean; isPee
}
// 如果有房间信息但WebSocket未连接,且不是正在连接状态
- // 可能是状态更新的时序问题,显示连接中状态
if (!isWebSocketConnected && !isConnecting) {
return {
type: 'connecting' as const,
@@ -63,6 +63,13 @@ const getConnectionStatus = (connection: { isWebSocketConnected?: boolean; isPee
}
if (isWebSocketConnected && isPeerConnected) {
+ if (transportMode === 'relay') {
+ return {
+ type: 'connected-relay' as const,
+ message: '服务器中继连接',
+ detail: 'P2P不可用,已自动切换到服务器中继传输',
+ };
+ }
return {
type: 'connected' as const,
message: 'P2P连接成功',
@@ -82,6 +89,8 @@ const getStatusColor = (type: string) => {
switch (type) {
case 'connected':
return 'text-green-600';
+ case 'connected-relay':
+ return 'text-blue-600';
case 'connecting':
case 'room-ready':
return 'text-yellow-600';
@@ -101,6 +110,8 @@ const StatusIcon = ({ type, className = 'w-3 h-3' }: { type: string; className?:
switch (type) {
case 'connected':
return
;
+ case 'connected-relay':
+ return ;
case 'connecting':
case 'room-ready':
return (
@@ -116,15 +127,17 @@ const StatusIcon = ({ type, className = 'w-3 h-3' }: { type: string; className?:
};
// 获取连接状态文字描述
-const getConnectionStatusText = (connection: { isWebSocketConnected?: boolean; isPeerConnected?: boolean; isConnecting?: boolean; error?: string | null }) => {
+const getConnectionStatusText = (connection: { isWebSocketConnected?: boolean; isPeerConnected?: boolean; isConnecting?: boolean; error?: string | null; transportMode?: string }) => {
const isWebSocketConnected = connection?.isWebSocketConnected || false;
const isPeerConnected = connection?.isPeerConnected || false;
const isConnecting = connection?.isConnecting || false;
const error = connection?.error || null;
+ const transportMode = connection?.transportMode || 'p2p';
const wsStatus = isWebSocketConnected ? 'WS已连接' : 'WS未连接';
- const rtcStatus = isPeerConnected ? 'RTC已连接' :
- isWebSocketConnected ? 'RTC等待连接' : 'RTC未连接';
+ const modeLabel = transportMode === 'relay' ? '中继' : 'P2P';
+ const rtcStatus = isPeerConnected ? `${modeLabel}已连接` :
+ isWebSocketConnected ? `${modeLabel}等待连接` : `${modeLabel}未连接`;
if (error) {
return `${wsStatus} ${rtcStatus} - 连接失败`;
@@ -135,6 +148,9 @@ const getConnectionStatusText = (connection: { isWebSocketConnected?: boolean; i
}
if (isPeerConnected) {
+ if (transportMode === 'relay') {
+ return `${wsStatus} ${rtcStatus} - 服务器中继`;
+ }
return `${wsStatus} ${rtcStatus} - P2P连接成功`;
}
@@ -153,6 +169,7 @@ export function ConnectionStatus(props: ConnectionStatusProps) {
isPeerConnected: webrtcState.isPeerConnected,
isConnecting: webrtcState.isConnecting,
error: webrtcState.error,
+ transportMode: webrtcState.transportMode,
};
const isConnected = webrtcState.isWebSocketConnected && webrtcState.isPeerConnected;
@@ -163,6 +180,7 @@ export function ConnectionStatus(props: ConnectionStatusProps) {
}
const status = getConnectionStatus(connection, currentRoom ?? null);
+ const isRelay = webrtcState.transportMode === 'relay';
if (compact) {
return (
@@ -182,10 +200,10 @@ export function ConnectionStatus(props: ConnectionStatusProps) {
|
- RTC
+ {isRelay ? '中继' : 'RTC'}
@@ -221,15 +239,19 @@ export function ConnectionStatus(props: ConnectionStatusProps) {
|
- RTC
+ {isRelay ? '中继' : 'RTC'}
- {connection.isPeerConnected ? '已连接' : '未连接'}
+ {connection.isPeerConnected
+ ? (isRelay ? '中继已连接' : '已连接')
+ : '未连接'}
diff --git a/chuan-next/src/hooks/connection/useSharedWebRTCManager.ts b/chuan-next/src/hooks/connection/useSharedWebRTCManager.ts
index a94f0b8..7123775 100644
--- a/chuan-next/src/hooks/connection/useSharedWebRTCManager.ts
+++ b/chuan-next/src/hooks/connection/useSharedWebRTCManager.ts
@@ -1,5 +1,5 @@
import { useCallback, useMemo } from 'react';
-import { useWebRTCStore, type WebRTCStateManager } from '../ui/webRTCStore';
+import { useWebRTCStore, type WebRTCStateManager, type TransportMode } from '../ui/webRTCStore';
import { useWebRTCDataChannelManager, WebRTCMessage } from './useWebRTCDataChannelManager';
import { useWebRTCTrackManager } from './useWebRTCTrackManager';
import { useWebRTCConnectionCore } from './useWebRTCConnectionCore';
@@ -17,6 +17,8 @@ export interface WebRTCConnection {
isPeerConnected: boolean;
error: string | null;
canRetry: boolean;
+ // 传输模式
+ transportMode: TransportMode;
// 操作方法
connect: (roomCode: string, role: 'sender' | 'receiver') => Promise;
@@ -61,6 +63,7 @@ export function useSharedWebRTCManager(): WebRTCConnection {
error: store.error,
canRetry: store.canRetry,
currentRoom: store.currentRoom,
+ transportMode: store.transportMode,
}),
updateState: store.updateState,
setCurrentRoom: store.setCurrentRoom,
@@ -87,6 +90,7 @@ export function useSharedWebRTCManager(): WebRTCConnection {
isPeerConnected: store.isPeerConnected,
error: store.error,
canRetry: store.canRetry,
+ transportMode: store.transportMode,
};
// 创建 createOfferNow 方法
@@ -115,6 +119,7 @@ export function useSharedWebRTCManager(): WebRTCConnection {
isPeerConnected: state.isPeerConnected,
error: state.error,
canRetry: state.canRetry,
+ transportMode: state.transportMode,
// 操作方法
connect: connectionCore.connect,
diff --git a/chuan-next/src/hooks/connection/useWebRTCConnectionCore.ts b/chuan-next/src/hooks/connection/useWebRTCConnectionCore.ts
index ffbc6e3..c433f15 100644
--- a/chuan-next/src/hooks/connection/useWebRTCConnectionCore.ts
+++ b/chuan-next/src/hooks/connection/useWebRTCConnectionCore.ts
@@ -3,12 +3,12 @@ import { useRef, useCallback } from 'react';
import { getWsUrl } from '@/lib/config';
import { getIceServersConfig } from '../settings/useIceServersConfig';
import { WebRTCStateManager } from '../ui/webRTCStore';
-import { WebRTCDataChannelManager, WebRTCMessage } from './useWebRTCDataChannelManager';
+import { WebRTCDataChannelManager } from './useWebRTCDataChannelManager';
import { WebRTCTrackManager } from './useWebRTCTrackManager';
/**
* WebRTC 核心连接管理器
- * 负责基础的 WebRTC 连接管理
+ * 负责基础的 WebRTC 连接管理,支持 P2P → WS Relay 自动降级
*/
export interface WebRTCConnectionCore {
// 连接到房间
@@ -48,6 +48,13 @@ export function useWebRTCConnectionCore(
// 用于跟踪是否是用户主动断开连接
const isUserDisconnecting = useRef(false);
+
+ // 中继降级相关
+ const relayWsRef = useRef(null);
+ const isRelayFallbackInProgress = useRef(false);
+ const p2pFailureTimeout = useRef(null);
+ // 标记是否已经发送过 relay-request(避免重复发送)
+ const relayRequestSent = useRef(false);
// 清理连接
const cleanup = useCallback((shouldNotifyDisconnect: boolean = false) => {
@@ -57,12 +64,26 @@ export function useWebRTCConnectionCore(
clearTimeout(timeoutRef.current);
timeoutRef.current = null;
}
+
+ if (p2pFailureTimeout.current) {
+ clearTimeout(p2pFailureTimeout.current);
+ p2pFailureTimeout.current = null;
+ }
if (pcRef.current) {
pcRef.current.close();
pcRef.current = null;
}
+ // 关闭中继连接
+ dataChannelManager.closeRelay();
+ if (relayWsRef.current) {
+ relayWsRef.current.close();
+ relayWsRef.current = null;
+ }
+ isRelayFallbackInProgress.current = false;
+ relayRequestSent.current = false;
+
// 在清理 WebSocket 之前发送断开通知
if (shouldNotifyDisconnect && wsRef.current && wsRef.current.readyState === WebSocket.OPEN) {
try {
@@ -83,7 +104,206 @@ export function useWebRTCConnectionCore(
currentRoom.current = null;
isUserDisconnecting.current = false; // 重置主动断开标志
- }, []);
+ }, [dataChannelManager]);
+
+ // ===== 连接到中继服务器(实际的 WS 连接逻辑) =====
+ const connectToRelay = useCallback(() => {
+ const room = currentRoom.current;
+ if (!room) {
+ console.warn('[ConnectionCore] 没有房间信息,无法连接中继');
+ return;
+ }
+
+ if (isRelayFallbackInProgress.current) {
+ console.log('[ConnectionCore] ⏭️ 中继连接已在进行中,跳过');
+ return;
+ }
+
+ if (isUserDisconnecting.current) {
+ console.log('[ConnectionCore] 用户正在主动断开,跳过中继');
+ return;
+ }
+
+ isRelayFallbackInProgress.current = true;
+ console.log('[ConnectionCore] 🔄 连接到中继服务器...');
+
+ // 更新状态:正在降级
+ stateManager.updateState({
+ error: null,
+ isConnecting: true,
+ canRetry: false,
+ });
+
+ const baseWsUrl = getWsUrl();
+ if (!baseWsUrl) {
+ console.error('[ConnectionCore] 无法获取 WS URL,中继连接失败');
+ isRelayFallbackInProgress.current = false;
+ stateManager.updateState({
+ error: '中继连接失败:无法获取服务器地址',
+ isConnecting: false,
+ canRetry: true,
+ });
+ return;
+ }
+
+ const relayUrl = `${baseWsUrl}/api/ws/relay?code=${room.code}&role=${room.role}`;
+ console.log('[ConnectionCore] 🌐 连接中继服务器:', relayUrl);
+
+ try {
+ const relayWs = new WebSocket(relayUrl);
+ relayWsRef.current = relayWs;
+
+ relayWs.binaryType = 'arraybuffer';
+
+ relayWs.onopen = () => {
+ console.log('[ConnectionCore] ✅ 中继 WebSocket 连接已建立');
+ };
+
+ // 统一的消息处理器:控制消息在这里处理,数据消息转发给 dataChannelManager
+ relayWs.onmessage = (event: MessageEvent) => {
+ // 文本消息:先检查是否是中继控制消息
+ if (typeof event.data === 'string') {
+ try {
+ const msg = JSON.parse(event.data);
+
+ // 中继服务的控制消息
+ if (msg.type === 'relay-ready') {
+ console.log('[ConnectionCore] 📡 中继已就绪, 对方在线:', msg.peer_connected);
+ if (msg.peer_connected) {
+ console.log('[ConnectionCore] 🎉 双方已通过中继连接,切换传输通道');
+ dataChannelManager.switchToRelay(relayWs);
+ isRelayFallbackInProgress.current = false;
+ stateManager.updateState({
+ isConnected: true,
+ isConnecting: false,
+ isPeerConnected: true,
+ error: null,
+ canRetry: false,
+ transportMode: 'relay',
+ });
+ }
+ // peer_connected === false: 等待对方也连接到中继
+ return;
+ }
+
+ if (msg.type === 'relay-peer-joined') {
+ console.log('[ConnectionCore] 🎉 对方已加入中继房间,切换传输通道');
+ dataChannelManager.switchToRelay(relayWs);
+ isRelayFallbackInProgress.current = false;
+ stateManager.updateState({
+ isConnected: true,
+ isConnecting: false,
+ isPeerConnected: true,
+ error: null,
+ canRetry: false,
+ transportMode: 'relay',
+ });
+ return;
+ }
+
+ if (msg.type === 'relay-peer-left') {
+ console.log('[ConnectionCore] 🔌 对方离开中继房间');
+ stateManager.updateState({
+ isPeerConnected: false,
+ isConnected: false,
+ error: '对方已离开房间',
+ canRetry: true,
+ });
+ return;
+ }
+
+ if (msg.type === 'error') {
+ console.error('[ConnectionCore] 中继服务错误:', msg.error);
+ isRelayFallbackInProgress.current = false;
+ stateManager.updateState({
+ error: `中继连接失败: ${msg.error}`,
+ isConnecting: false,
+ canRetry: true,
+ });
+ return;
+ }
+ } catch {
+ // 不是合法 JSON 或不是控制消息,当作数据消息处理
+ }
+ }
+
+ // 非控制消息 → 交给 dataChannelManager 分发给业务层
+ dataChannelManager.handleRelayMessage(event);
+ };
+
+ relayWs.onerror = (error) => {
+ console.error('[ConnectionCore] ❌ 中继 WebSocket 错误:', error);
+ isRelayFallbackInProgress.current = false;
+ stateManager.updateState({
+ error: 'WS 中继连接失败,请重试',
+ isConnecting: false,
+ canRetry: true,
+ });
+ };
+
+ relayWs.onclose = (event) => {
+ console.log('[ConnectionCore] 🔌 中继 WebSocket 关闭:', event.code, event.reason);
+ if (relayWsRef.current === relayWs) {
+ relayWsRef.current = null;
+ }
+ isRelayFallbackInProgress.current = false;
+
+ // 如果不是用户主动断开,且当前是中继模式
+ if (!isUserDisconnecting.current && stateManager.getState().transportMode === 'relay') {
+ stateManager.updateState({
+ isConnected: false,
+ isPeerConnected: false,
+ error: '中继连接断开',
+ canRetry: true,
+ });
+ }
+ };
+
+ } catch (error) {
+ console.error('[ConnectionCore] 创建中继连接失败:', error);
+ isRelayFallbackInProgress.current = false;
+ stateManager.updateState({
+ error: '无法建立中继连接,请重试',
+ isConnecting: false,
+ canRetry: true,
+ });
+ }
+ }, [stateManager, dataChannelManager]);
+
+ // ===== 发起中继降级(通知对方 + 自己连接) =====
+ const initiateRelayFallback = useCallback(() => {
+ if (relayRequestSent.current || isRelayFallbackInProgress.current) {
+ console.log('[ConnectionCore] ⏭️ 中继降级已发起/进行中,跳过');
+ return;
+ }
+
+ const room = currentRoom.current;
+ if (!room) {
+ console.warn('[ConnectionCore] 没有房间信息,无法降级');
+ return;
+ }
+
+ if (isUserDisconnecting.current) {
+ console.log('[ConnectionCore] 用户正在主动断开,跳过降级');
+ return;
+ }
+
+ console.log('[ConnectionCore] 🔄 P2P 连接失败,通过信令通知对方切换中继...');
+ relayRequestSent.current = true;
+
+ // 通过信令 WS 通知对方也连接到中继
+ const ws = wsRef.current;
+ if (ws && ws.readyState === WebSocket.OPEN) {
+ ws.send(JSON.stringify({
+ type: 'relay-request',
+ payload: { reason: 'P2P连接失败' }
+ }));
+ console.log('[ConnectionCore] 📤 已通过信令通知对方切换中继');
+ }
+
+ // 自己也连接到中继
+ connectToRelay();
+ }, [connectToRelay]);
// 创建 PeerConnection 和相关设置
const createPeerConnection = useCallback((ws: WebSocket, role: 'sender' | 'receiver', isReconnect: boolean = false) => {
@@ -138,10 +358,15 @@ export function useWebRTCConnectionCore(
case 'connected':
case 'completed':
console.log('[ConnectionCore] ✅ ICE连接成功');
+ // ICE 连接成功,清除降级定时器
+ if (p2pFailureTimeout.current) {
+ clearTimeout(p2pFailureTimeout.current);
+ p2pFailureTimeout.current = null;
+ }
break;
case 'failed':
- console.error('[ConnectionCore] ❌ ICE连接失败');
- stateManager.updateState({ error: 'ICE连接失败,可能是网络防火墙阻止了连接', isConnecting: false, canRetry: true });
+ console.error('[ConnectionCore] ❌ ICE连接失败,启动中继降级');
+ initiateRelayFallback();
break;
case 'disconnected':
console.log('[ConnectionCore] 🔌 ICE连接断开');
@@ -158,22 +383,38 @@ export function useWebRTCConnectionCore(
case 'connecting':
console.log('[ConnectionCore] 🔄 WebRTC正在连接中...');
stateManager.updateState({ isPeerConnected: false });
+
+ // 设置 P2P 连接超时:15 秒后如果还没连上就降级
+ if (p2pFailureTimeout.current) {
+ clearTimeout(p2pFailureTimeout.current);
+ }
+ p2pFailureTimeout.current = setTimeout(() => {
+ if (pcRef.current && pcRef.current.connectionState !== 'connected') {
+ console.log('[ConnectionCore] ⏰ P2P 连接超时(15秒),启动中继降级');
+ initiateRelayFallback();
+ }
+ }, 15000);
break;
case 'connected':
console.log('[ConnectionCore] 🎉 WebRTC P2P连接已完全建立,可以进行媒体传输');
+ // 清除降级定时器
+ if (p2pFailureTimeout.current) {
+ clearTimeout(p2pFailureTimeout.current);
+ p2pFailureTimeout.current = null;
+ }
// 确保所有连接状态都正确更新
stateManager.updateState({
isWebSocketConnected: true,
isConnected: true,
isPeerConnected: true,
error: null,
- canRetry: false
+ canRetry: false,
+ transportMode: 'p2p',
});
// 如果是重新连接,触发数据同步
if (isReconnect) {
console.log('[ConnectionCore] 🔄 检测到重新连接,触发数据同步');
- // 发送同步请求消息
setTimeout(() => {
const dc = pcRef.current?.createDataChannel('sync-channel');
if (dc && dc.readyState === 'open') {
@@ -184,12 +425,14 @@ export function useWebRTCConnectionCore(
console.log('[ConnectionCore] 📤 发送数据同步请求');
dc.close();
}
- }, 500); // 等待数据通道完全稳定
+ }, 500);
}
break;
case 'failed':
- console.error('[ConnectionCore] ❌ WebRTC连接失败');
- stateManager.updateState({ error: 'WebRTC连接失败,请检查网络设置或重试', isPeerConnected: false, canRetry: true });
+ console.error('[ConnectionCore] ❌ WebRTC连接失败,启动中继降级');
+ stateManager.updateState({ isPeerConnected: false });
+ // P2P 连接失败,自动降级到中继
+ initiateRelayFallback();
break;
case 'disconnected':
console.log('[ConnectionCore] 🔌 WebRTC连接已断开');
@@ -207,7 +450,7 @@ export function useWebRTCConnectionCore(
console.log('[ConnectionCore] ✅ PeerConnection创建完成,角色:', role, '是否重新连接:', isReconnect);
return pc;
- }, [stateManager, dataChannelManager]);
+ }, [stateManager, dataChannelManager, initiateRelayFallback]);
// 连接到房间
const connect = useCallback(async (roomCode: string, role: 'sender' | 'receiver') => {
@@ -278,11 +521,10 @@ export function useWebRTCConnectionCore(
console.log('[ConnectionCore] 👥 对方已加入房间,角色:', message.payload?.role);
if (role === 'sender' && message.payload?.role === 'receiver') {
console.log('[ConnectionCore] 🚀 接收方已连接,发送方开始建立P2P连接');
- // 确保WebSocket连接状态正确更新
+ // 标记对方已加入,但 isPeerConnected 在 P2P/Relay 真正连通后才设为 true
stateManager.updateState({
isWebSocketConnected: true,
isConnected: true,
- isPeerConnected: true // 标记对方已加入,可以开始P2P
});
// 如果是重新连接,先清理旧的PeerConnection
@@ -308,11 +550,10 @@ export function useWebRTCConnectionCore(
}
} else if (role === 'receiver' && message.payload?.role === 'sender') {
console.log('[ConnectionCore] 🚀 发送方已连接,接收方准备接收P2P连接');
- // 确保WebSocket连接状态正确更新
+ // 标记对方已加入,但 isPeerConnected 在 P2P/Relay 真正连通后才设为 true
stateManager.updateState({
isWebSocketConnected: true,
isConnected: true,
- isPeerConnected: true // 标记对方已加入
});
// 如果是重新连接,先清理旧的PeerConnection
@@ -454,6 +695,12 @@ export function useWebRTCConnectionCore(
stateManager.updateState({ error: message.error, isConnecting: false, canRetry: true });
break;
+ case 'relay-request':
+ // 对方的 P2P 失败,请求双方都切换到中继模式
+ console.log('[ConnectionCore] 📨 收到对方的中继降级请求');
+ connectToRelay();
+ break;
+
case 'disconnection':
console.log('[ConnectionCore] 🔌 对方主动断开连接');
// 对方断开连接的处理
@@ -509,7 +756,7 @@ export function useWebRTCConnectionCore(
canRetry: true
});
}
- }, [stateManager, cleanup, createPeerConnection]);
+ }, [stateManager, cleanup, createPeerConnection, connectToRelay]);
// 断开连接
const disconnect = useCallback((shouldNotifyDisconnect: boolean = false) => {
diff --git a/chuan-next/src/hooks/connection/useWebRTCDataChannelManager.ts b/chuan-next/src/hooks/connection/useWebRTCDataChannelManager.ts
index e25f7b8..197214c 100644
--- a/chuan-next/src/hooks/connection/useWebRTCDataChannelManager.ts
+++ b/chuan-next/src/hooks/connection/useWebRTCDataChannelManager.ts
@@ -14,16 +14,25 @@ export type DataHandler = (data: ArrayBuffer) => void;
/**
* WebRTC 数据通道管理器
- * 负责数据通道的创建和管理
+ * 负责数据通道的创建和管理,支持 P2P DataChannel 和 WS Relay 两种传输模式
*/
export interface WebRTCDataChannelManager {
- // 创建数据通道
+ // 创建数据通道 (P2P 模式)
createDataChannel: (pc: RTCPeerConnection, role: 'sender' | 'receiver', isReconnect?: boolean) => void;
- // 发送消息
+ // 切换到 WS 中继模式(仅设置发送引用,不设置事件监听)
+ switchToRelay: (relayWs: WebSocket) => void;
+
+ // 关闭中继连接
+ closeRelay: () => void;
+
+ // 处理中继收到的数据消息(由 ConnectionCore 的 onmessage 调用)
+ handleRelayMessage: (event: MessageEvent) => void;
+
+ // 发送消息(自动选择可用通道)
sendMessage: (message: WebRTCMessage, channel?: string) => boolean;
- // 发送二进制数据
+ // 发送二进制数据(自动选择可用通道)
sendData: (data: ArrayBuffer) => boolean;
// 注册消息处理器
@@ -32,27 +41,40 @@ export interface WebRTCDataChannelManager {
// 注册数据处理器
registerDataHandler: (channel: string, handler: DataHandler) => () => void;
- // 获取数据通道状态
+ // 获取数据通道状态(兼容 RTCDataChannelState)
getChannelState: () => RTCDataChannelState;
- // 处理数据通道消息
+ // 处理数据通道消息 (P2P)
handleDataChannelMessage: (event: MessageEvent) => void;
}
/**
* WebRTC 数据通道管理 Hook
* 负责数据通道的创建和管理,处理数据通道消息的发送和接收
+ * 支持 P2P DataChannel 和 WS Relay 两种传输模式,对上层透明
*/
export function useWebRTCDataChannelManager(
stateManager: WebRTCStateManager
): WebRTCDataChannelManager {
const dcRef = useRef(null);
+ // WS 中继通道
+ const relayWsRef = useRef(null);
// 多通道消息处理器
const messageHandlers = useRef