From 1a6a7369b95797bf0496a6220be9bfdb7bc89824 Mon Sep 17 00:00:00 2001 From: MatrixSeven Date: Sun, 1 Mar 2026 00:08:29 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E6=88=BF=E9=97=B4=E9=AA=8C?= =?UTF-8?q?=E8=AF=81=E5=B7=A5=E5=85=B7=E5=87=BD=E6=95=B0=EF=BC=8C=E5=8C=85?= =?UTF-8?q?=E5=90=AB=E6=88=BF=E9=97=B4=E4=BB=A3=E7=A0=81=E6=A0=BC=E5=BC=8F?= =?UTF-8?q?=E9=AA=8C=E8=AF=81=E5=92=8C=E6=88=BF=E9=97=B4=E7=8A=B6=E6=80=81?= =?UTF-8?q?=E6=A3=80=E6=9F=A5=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ARCHITECTURE.md | 737 ++++++++++ FRONTEND_ANALYSIS.md | 471 +++++++ PROTOCOL_ANALYSIS.md | 1237 +++++++++++++++++ .../src/components/WebRTCConnectionStatus.tsx | 185 --- .../src/components/WebRTCFileTransfer.tsx | 348 +---- .../webrtc/WebRTCDesktopReceiver.tsx | 106 +- .../components/webrtc/WebRTCFileReceive.tsx | 53 +- .../components/webrtc/WebRTCFileUpload.tsx | 11 +- .../components/webrtc/WebRTCTextReceiver.tsx | 34 +- chuan-next/src/hooks/connection/index.ts | 1 - .../hooks/connection/useConnectionState.ts | 137 -- .../src/hooks/connection/useRoomConnection.ts | 65 +- .../connection/useSharedWebRTCManager.ts | 38 +- .../connection/useWebRTCConnectionCore.ts | 2 +- .../connection/useWebRTCDataChannelManager.ts | 2 +- .../hooks/connection/useWebRTCStateManager.ts | 78 -- .../hooks/connection/useWebRTCTrackManager.ts | 2 +- .../hooks/file-transfer/useFileListSync.ts | 10 +- .../file-transfer/useFileStateManager.ts | 10 +- .../file-transfer/useFileTransferBusiness.ts | 11 +- chuan-next/src/hooks/ui/webRTCStore.ts | 22 +- chuan-next/src/lib/api-utils.ts | 144 -- chuan-next/src/lib/client-api.ts | 119 -- chuan-next/src/lib/room-utils.ts | 94 ++ chuan-next/src/types/index.ts | 21 +- 25 files changed, 2697 insertions(+), 1241 deletions(-) create mode 100644 ARCHITECTURE.md create mode 100644 FRONTEND_ANALYSIS.md create mode 100644 PROTOCOL_ANALYSIS.md delete mode 100644 chuan-next/src/components/WebRTCConnectionStatus.tsx delete mode 100644 chuan-next/src/hooks/connection/useConnectionState.ts delete mode 100644 chuan-next/src/hooks/connection/useWebRTCStateManager.ts delete mode 100644 chuan-next/src/lib/api-utils.ts delete mode 100644 chuan-next/src/lib/client-api.ts create mode 100644 chuan-next/src/lib/room-utils.ts diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md new file mode 100644 index 0000000..e73f3ca --- /dev/null +++ b/ARCHITECTURE.md @@ -0,0 +1,737 @@ +# 文件快传(Chuan)— 系统架构与技术设计文档 + +> 最后更新:2025-08-02 + +--- + +## 一、项目总览 + +**文件快传(Chuan)** 是一个基于 **WebRTC P2P** 的文件传输应用。核心设计理念:**所有实际数据(文件、文字、桌面画面)均通过 WebRTC 点对点直传,服务器仅承担信令中继和房间管理职责**。 + +### 技术栈 + +| 层级 | 技术选型 | +|------|---------| +| 后端 | Go 1.21 · chi/v5 路由 · gorilla/websocket | +| 前端 | Next.js 15 · React 19 · TypeScript · Tailwind CSS 4 | +| 状态管理 | Zustand 5 + React useState/useRef | +| UI 组件 | shadcn/ui (Radix UI) · Lucide Icons | +| P2P 通信 | WebRTC DataChannel(文件/文字)· MediaStream(桌面共享)| +| 部署 | 双模式:Next.js 开发模式 / Go 嵌入前端静态文件 | + +### 核心特性 + +| 功能 | 实现方式 | +|------|---------| +| 文件传输 | WebRTC DataChannel · 256KB 分块 · CRC32 校验 · ACK 确认 | +| 文本消息 | WebRTC DataChannel · 实时双向同步 · 打字状态 | +| 桌面共享 | WebRTC MediaStream · getDisplayMedia · renegotiation | +| ICE 配置 | 默认 5 个 STUN · 支持自定义 STUN/TURN · localStorage 持久化 | + +--- + +## 二、系统架构 + +### 2.1 整体架构图 + +``` +┌──────────────────────────────────────────────────────────────────────┐ +│ Go 后端 (:8080) │ +│ ┌──────────────┐ ┌───────────────────┐ ┌─────────────────────┐ │ +│ │ REST API │ │ WebSocket 信令 │ │ 静态文件服务 │ │ +│ │ /api/* │ │ /api/ws/webrtc │ │ go:embed frontend │ │ +│ │ │ │ /ws/webrtc │ │ │ │ +│ │ - create-room │ │ │ │ SPA 回退 index.html │ │ +│ │ - room-info │ │ 纯中继 转发消息 │ │ │ │ +│ └──────────────┘ └───────────────────┘ └─────────────────────┘ │ +│ │ │ +│ ┌───────────────┼───────────────┐ │ +│ │ 内存房间管理 (sync.RWMutex) │ │ +│ │ map[string]*WebRTCRoom │ │ +│ │ 1小时过期 · 5分钟定时清理 │ │ +│ └───────────────────────────────┘ │ +└──────────────────────────────────────────────────────────────────────┘ + │ WebSocket │ WebSocket + │ (信令: offer/answer/ice-candidate) │ + ▼ ▼ +┌───────────────┐ WebRTC P2P ┌───────────────┐ +│ 发送方浏览器 │ ◄══════════════════════════► │ 接收方浏览器 │ +│ │ DataChannel (文件/文字) │ │ +│ Next.js App │ MediaStream (桌面共享) │ Next.js App │ +└───────────────┘ └───────────────┘ +``` + +### 2.2 数据流向 + +``` +1. 控制平面 (HTTP): 浏览器 ──── REST API ────→ Go 后端 (房间创建/查询) +2. 信令平面 (WS): 浏览器 ←──── WebSocket ──→ Go 后端 (SDP/ICE 交换) +3. 数据平面 (P2P): 浏览器 ◄═══ DataChannel ══► 浏览器 (文件/文字直传) +4. 媒体平面 (P2P): 浏览器 ◄═══ MediaStream ══► 浏览器 (桌面画面直传) +``` + +**关键设计:数据平面完全绕过服务器,文件和文字内容不经服务器中转。** + +--- + +## 三、后端设计 + +### 3.1 目录结构 + +``` +cmd/ +├── main.go # 入口:参数解析 → 配置 → 路由 → 启动服务器 +├── config.go # 配置管理:命令行 > 环境变量 > .chuan.env > 默认值 +├── router.go # chi 路由注册 + 中间件 + 前端静态服务 +└── server.go # HTTP Server 封装 + 优雅关闭 (SIGINT/SIGTERM) + +internal/ +├── handlers/ +│ └── handlers.go # HTTP/WebSocket 请求处理(薄层,委托给 service) +├── models/ +│ └── models.go # 数据模型:WebRTCRoom、WebRTCClient、RoomStatus +├── services/ +│ └── webrtc_service.go # 核心信令服务:房间管理 + WebSocket 消息转发 +└── web/ + └── frontend.go # go:embed 嵌入前端 + SPA 回退 +``` + +### 3.2 API 端点 + +| 方法 | 路径 | 功能 | 请求/响应 | +|------|------|------|-----------| +| `POST` | `/api/create-room` | 创建房间 | `{}` → `{success, code, message}` | +| `GET` | `/api/room-info?code=XXX` | 查询房间状态 | → `{success, status: RoomStatus}` | +| `GET` | `/api/webrtc-room-status?code=XXX` | 同上(别名)| 同上 | +| `WS` | `/api/ws/webrtc?code=&role=` | WebRTC 信令 | WebSocket 双向 | +| `WS` | `/ws/webrtc?code=&role=` | 同上(兼容路径)| 同上 | +| `GET` | `/*` | 前端静态文件 | SPA 回退 | + +### 3.3 房间管理 + +- **房间代码**:6 位,字符集 `123456789ABCDEFGHIJKLMNPQRSTUVWXYZ`(排除 0 和 O,避免混淆) +- **房间容量**:最多 2 人(1 sender + 1 receiver) +- **过期策略**:创建后 1 小时过期,每 5 分钟后台清理 +- **存储方式**:纯内存 `map[string]*WebRTCRoom` + `sync.RWMutex`,无数据库 + +### 3.4 WebSocket 信令协议 + +#### 连接 URL +``` +ws[s]://host/api/ws/webrtc?code=ROOM_CODE&role=sender|receiver&channel=shared +``` + +#### 服务端 → 客户端 + +| type | payload | 触发时机 | +|------|---------|---------| +| `peer-joined` | `{ role }` | 对方加入房间 | +| `disconnection` | `{ role, message }` | 对方断开连接 | +| `error` | `{ message }` | 房间不存在 / 已满 / 参数无效 | + +#### 客户端 ↔ 客户端(经服务端纯转发) + +| type | payload | 说明 | +|------|---------|------| +| `offer` | `RTCSessionDescription` | SDP Offer | +| `answer` | `RTCSessionDescription` | SDP Answer | +| `ice-candidate` | `RTCIceCandidate` | ICE 候选地址 | + +**服务端对 offer/answer/ice-candidate 消息不做任何解析,仅中继转发给房间内另一方。** + +--- + +## 四、前端设计(重点) + +### 4.1 目录结构 + +``` +chuan-next/src/ +├── app/ # Next.js App Router +│ ├── page.tsx # 入口页(SSG) +│ ├── layout.tsx # 根布局(字体、Toast、Umami 统计) +│ ├── globals.css # 全局样式 + 动画定义 +│ ├── HomePage.tsx # 主页面组件(Tab 管理 + WebRTC 状态) +│ ├── HomePageWrapper.tsx # Suspense 包裹(SSR 兼容) +│ └── api/ # Next.js API Routes(开发模式代理) +│ ├── create-room/route.ts # → GO_BACKEND_URL/api/create-room +│ ├── room-info/route.ts # → GO_BACKEND_URL/api/room-info +│ ├── room-status/route.ts # → GO_BACKEND_URL/api/room-status +│ ├── create-text-room/route.ts +│ ├── get-text-content/route.ts +│ └── update-files/route.ts +│ +├── components/ # UI 组件 +│ ├── WebRTCFileTransfer.tsx # 文件传输整合组件(659行) +│ ├── WebRTCTextImageTransfer.tsx # 文字+图片传输整合组件 +│ ├── DesktopShare.tsx # 桌面共享整合组件 +│ ├── Hero.tsx # 页头(标题、GitHub 链接) +│ ├── webrtc/ # WebRTC 功能子组件 +│ │ ├── WebRTCFileUpload.tsx # 文件发送 UI +│ │ ├── WebRTCFileReceive.tsx # 文件接收 UI +│ │ ├── WebRTCTextSender.tsx # 文字发送 UI +│ │ ├── WebRTCTextReceiver.tsx # 文字接收 UI +│ │ ├── WebRTCDesktopSender.tsx # 桌面共享发送 UI +│ │ ├── WebRTCDesktopReceiver.tsx # 桌面共享接收 UI +│ │ └── WebRTCSettings.tsx # ICE 服务器配置 UI +│ └── ui/ # 基础 UI 组件 (shadcn/ui) +│ ├── button.tsx # 按钮(6种 variant) +│ ├── tabs.tsx # 标签页(Radix Tabs) +│ ├── input.tsx # 输入框 +│ ├── card.tsx # 卡片 +│ ├── dialog.tsx # 对话框 +│ ├── progress.tsx # 进度条 +│ ├── textarea.tsx # 文本域 +│ ├── toast.tsx # Toast (Radix) +│ ├── toast-simple.tsx # 轻量 Toast (自定义) +│ ├── toaster.tsx # Toast 渲染 +│ └── confirm-dialog.tsx # 确认对话框 +│ +├── hooks/ # 自定义 Hooks(核心逻辑层) +│ ├── index.ts # 统一导出 +│ ├── connection/ # WebRTC 连接管理 +│ │ ├── useSharedWebRTCManager.ts # 整合入口 +│ │ ├── useWebRTCConnectionCore.ts # 核心连接(WS + PeerConnection) +│ │ ├── useWebRTCDataChannelManager.ts # DataChannel 管理 + 消息路由 +│ │ ├── useWebRTCTrackManager.ts # MediaStream 轨道管理 +│ │ ├── useWebRTCStateManager.ts # Zustand store 封装 +│ │ ├── useRoomConnection.ts # 加入房间逻辑 +│ │ ├── useConnectionState.ts # 连接状态变化处理 +│ │ └── useWebRTCSupport.ts # 浏览器 WebRTC 检测 +│ ├── file-transfer/ # 文件传输业务 +│ │ ├── useFileTransferBusiness.ts # 核心:分块传输 + CRC32 + ACK +│ │ ├── useFileListSync.ts # 文件列表实时同步 +│ │ └── useFileStateManager.ts # 文件状态(选中/下载/进度) +│ ├── text-transfer/ # 文字传输业务 +│ │ └── useTextTransferBusiness.ts # 实时文字同步 + 打字状态 +│ ├── desktop-share/ # 桌面共享业务 +│ │ └── useDesktopShareBusiness.ts # getDisplayMedia + 轨道管理 +│ ├── settings/ # 设置 +│ │ ├── useIceServersConfig.ts # ICE 服务器配置 + localStorage +│ │ └── useWebRTCConfigSync.ts # 配置变更监听 +│ └── ui/ # UI 相关 +│ ├── webRTCStore.ts # Zustand 全局状态 +│ ├── useURLHandler.ts # URL 参数管理 +│ ├── useTabNavigation.ts # Tab 切换管理 +│ └── useConfirmDialog.ts # 确认对话框 Hook +│ +├── lib/ # 工具库 +│ ├── config.ts # 环境配置(API URL / WS URL 动态计算) +│ ├── utils.ts # cn() 样式合并 +│ ├── client-api.ts # ClientAPI 封装 +│ ├── api-utils.ts # apiFetch() 统一请求 +│ └── webrtc-support.ts # WebRTC 支持检测 +│ +└── types/ + └── index.ts # 全局类型定义 +``` + +### 4.2 组件层次结构 + +``` +RootLayout (layout.tsx) + └─ ToastProvider (toast-simple.tsx) + └─ HomePageWrapper (Suspense) + └─ HomePage + ├─ Hero # 页头 + ├─ WebRTCUnsupportedModal # WebRTC 不支持提示 + ├─ ConfirmDialog # Tab 切换确认 + └─ Tabs (5个标签) + │ + ├─ [Tab: webrtc] WebRTCFileTransfer + │ ├─ 模式切换 (发送/接收) + │ ├─ [发送模式] WebRTCFileUpload + │ │ ├─ 拖拽上传区域 / 文件列表 + │ │ ├─ RoomInfoDisplay (取件码 + 二维码 + 复制) + │ │ ├─ ConnectionStatus (连接状态) + │ │ └─ 传输进度条 + │ └─ [接收模式] WebRTCFileReceive + │ ├─ 取件码输入 (6位) + │ ├─ ConnectionStatus (连接状态) + │ └─ 文件列表 + 下载按钮 + │ + ├─ [Tab: message] WebRTCTextImageTransfer + │ ├─ 模式切换 (发送/接收) + │ ├─ [发送模式] WebRTCTextSender + │ │ ├─ Textarea 编辑器 + │ │ ├─ 图片发送按钮 + │ │ └─ RoomInfoDisplay + │ └─ [接收模式] WebRTCTextReceiver + │ ├─ 取件码输入 + │ └─ 实时文字显示 + 图片接收 + │ + ├─ [Tab: desktop] DesktopShare + │ ├─ 模式切换 (共享/查看) + │ ├─ [共享模式] WebRTCDesktopSender + │ │ └─ 选择屏幕 → 开始共享 + │ └─ [查看模式] WebRTCDesktopReceiver + │ └─ DesktopViewer (video 标签) + │ + ├─ [Tab: wechat] WeChatGroup + │ └─ 微信群二维码 + │ + └─ [Tab: settings] WebRTCSettings + └─ ICE 服务器配置表单 +``` + +### 4.3 Hooks 架构(核心设计) + +前端逻辑层采用 **分层 Hooks 架构**,自底向上组合: + +``` + ┌─────────────────────────────┐ + │ Zustand Store │ ← 最底层:全局状态 + │ (webRTCStore.ts) │ + │ isConnected, isConnecting │ + │ currentRoom, error │ + └──────────────┬──────────────┘ + │ + ┌──────────────────────┼──────────────────────┐ + │ │ │ + ┌────────▼────────┐ ┌─────────▼──────────┐ ┌───────▼─────────┐ + │ StateManager │ │ DataChannelManager │ │ TrackManager │ + │ (Zustand 封装) │ │ (消息路由 + 收发) │ │ (媒体轨道管理) │ + └────────┬────────┘ └─────────┬──────────┘ └───────┬─────────┘ + │ │ │ + └──────────────────────┼──────────────────────┘ + │ + ┌──────────────▼──────────────┐ + │ ConnectionCore │ ← WebSocket + PeerConnection + │ (useWebRTCConnectionCore) │ 信令处理 + ICE 交换 + └──────────────┬──────────────┘ + │ + ┌──────────────▼──────────────┐ + │ SharedWebRTCManager │ ← 整合入口(4合1) + │ (useSharedWebRTCManager) │ 返回统一 WebRTCConnection + └──────────────┬──────────────┘ + │ + ┌────────────────────────┼────────────────────────┐ + │ │ │ + ┌────────▼────────┐ ┌──────────▼──────────┐ ┌────────▼────────┐ + │ FileTransfer │ │ TextTransfer │ │ DesktopShare │ + │ Business │ │ Business │ │ Business │ + │ (文件分块+校验) │ │ (实时同步文字) │ │ (屏幕采集+推流) │ + └────────┬────────┘ └──────────┬──────────┘ └────────┬────────┘ + │ │ │ + └────────────────────────┼────────────────────────┘ + │ + ┌──────────────▼──────────────┐ + │ 业务级组件 (TSX) │ + │ WebRTCFileTransfer.tsx 等 │ + └─────────────────────────────┘ +``` + +#### 4.3.1 连接层 — `useWebRTCConnectionCore` + +**职责**:管理 WebSocket 信令连接 + RTCPeerConnection 生命周期。 + +``` +connect(code, role) + │ + ├─ 1. WebSocket 连接到 ws://host/api/ws/webrtc?code=XXX&role=YYY + │ + ├─ 2. 收到 "peer-joined" → 创建 RTCPeerConnection + │ └─ ICE 服务器从 getIceServersConfig() 获取 + │ + ├─ 3. Sender 创建 DataChannel → 创建 Offer + │ Receiver 等待 ondatachannel + │ + ├─ 4. offer/answer/ice-candidate 通过 WebSocket 交换 + │ + ├─ 5. ICE 连接建立 → DataChannel open → 连接完成 + │ + └─ 6. 断开时:发送 disconnection → 清理 PeerConnection +``` + +#### 4.3.2 数据通道层 — `useWebRTCDataChannelManager` + +**职责**:管理 DataChannel 消息的路由分发。 + +**核心设计:多 channel 路由** + +所有 JSON 消息通过 `channel` 字段路由到不同的业务处理器: + +```typescript +// 消息格式 +{ + channel: "file-transfer" | "text-transfer" | "desktop-share", + type: "具体消息类型", + payload: { ... } +} +``` + +``` +DataChannel.onmessage + │ + ├─ typeof data === 'string' → JSON.parse + │ ├─ channel === 'file-transfer' → fileTransferHandler + │ ├─ channel === 'text-transfer' → textTransferHandler + │ └─ channel === 'desktop-share' → desktopShareHandler + │ + └─ typeof data === ArrayBuffer → 二进制数据 + └─ 优先路由到 'file-transfer' handler(文件块数据) +``` + +**注意**:三个功能(文件/文字/桌面)共享同一个 DataChannel(`shared-channel`),而非各自创建独立通道。 + +#### 4.3.3 轨道管理层 — `useWebRTCTrackManager` + +**职责**:管理 MediaStream 轨道(仅桌面共享使用)。 + +- `addTrack(track, stream)` — 添加视频/音频轨道 +- `removeTrack(sender)` — 移除轨道 +- `createOfferNow()` — 添加轨道后触发重协商 +- `onTrack` 回调 — 接收远程媒体流 + +### 4.4 状态管理设计 + +采用 **三层状态模型**: + +| 层级 | 技术 | 用途 | 示例 | +|------|------|------|------| +| 全局共享状态 | Zustand | WebRTC 连接状态 | `isConnected`, `currentRoom`, `error` | +| 组件级状态 | React useState | UI 交互状态 | `selectedFiles`, `mode`, `progress` | +| 引用状态 | React useRef | 回调/缓冲区/定时器 | `receiveBufferRef`, `messageHandlers`, 防抖 timer | + +#### Zustand Store 结构 + +```typescript +interface WebRTCState { + // 连接状态 + isConnected: boolean; // 总体连接状态 + isConnecting: boolean; // 正在连接中 + isWebSocketConnected: boolean; // WebSocket 层连接 + isPeerConnected: boolean; // PeerConnection 层连接 + + // 错误状态 + error: string | null; + canRetry: boolean; + + // 房间信息 + currentRoom: { code: string; role: 'sender' | 'receiver' } | null; + + // Actions + updateState(partial: Partial): void; + setCurrentRoom(room: { code: string; role: string } | null): void; + resetToInitial(): void; +} +``` + +### 4.5 文件传输详细设计 + +#### 4.5.1 DataChannel 消息协议 + +**file-transfer channel 消息类型:** + +| 方向 | type | payload | 说明 | +|------|------|---------|------| +| S→R | `file-list` | `FileInfo[]` | 发送方文件列表(实时同步) | +| R→S | `file-request` | `{ fileId, fileName }` | 接收方请求下载 | +| S→R | `file-metadata` | `{ id, name, size, type }` | 文件元信息 | +| S→R | `file-chunk-info` | `{ fileId, chunkIndex, totalChunks, checksum }` | 块元信息(含 CRC32) | +| S→R | *(二进制)* | `ArrayBuffer` (≤256KB) | 文件块数据 | +| R→S | `file-chunk-ack` | `{ fileId, chunkIndex, success, checksum }` | 块确认(含校验结果) | +| S→R | `file-complete` | `{ fileId }` | 文件传输完成 | + +#### 4.5.2 传输参数 + +| 参数 | 值 | 说明 | +|------|-----|------| +| 块大小 | 256 KB | 每个 chunk 的最大字节数 | +| 最大重试 | 5 次 | 单个 chunk 校验失败后的重试上限 | +| 退避策略 | 指数退避 | 重试间隔指数增长 | +| 校验算法 | CRC32 | 每个 chunk 独立校验 | +| 流控 | 自适应 | 根据平均传输速度动态调整发送间隔 | +| DataChannel 配置 | `ordered: true, maxRetransmits: 3` | 有序可靠传输 | + +#### 4.5.3 完整传输时序 + +``` +发送方 信令服务器 接收方 + │ │ │ + │ 1. POST /api/create-room │ │ + │ ──────────────────────────────────► │ │ + │ ◄── {success: true, code: "AB12CD"} │ │ + │ │ │ + │ 2. WS connect (role=sender) │ │ + │ ═══════════════════════════════════► │ │ + │ │ │ + │ │ 3. GET /api/room-info?code=... │ + │ │ ◄────────────────────────────── │ + │ │ ── {success, status} ─────────► │ + │ │ │ + │ │ 4. WS connect (role=receiver) │ + │ │ ◄═══════════════════════════════ │ + │ │ │ + │ 5. ◄── peer-joined ── │ ── peer-joined ──► │ + │ │ │ + │ 6. 创建 PeerConnection + DataChannel │ + │ 7. 创建 SDP Offer │ + │ ═══ offer ═══════════════════════► │ ═══ offer ═══════════════════► │ + │ │ │ + │ │ 8. 创建 PeerConnection │ + │ │ 9. 设置 Remote/Local SDP │ + │ ◄═══ answer ══════════════════════ │ ◄═══ answer ═════════════════ │ + │ │ │ + │ 10. ICE Candidate 交换 ◄═══════════╋══════════════════════════════► │ + │ │ │ + │ ══════════ DataChannel OPEN ═══════╪══════════════════════════════ │ + │ │ │ + │ 11. file-list ─────────────────────┼────────────────────────────────► │ + │ [{id, name, size, type}, ...] │ │ + │ │ │ + │ ◄─────────────────────────────────┼──── 12. file-request ────────── │ + │ {fileId, fileName} │ │ + │ │ │ + │ 13. file-metadata ────────────────┼────────────────────────────────► │ + │ {id, name, size, type} │ │ + │ │ │ + │ ╔══ 循环每个 chunk ═══════════════╪════════════════════════════════╗ │ + │ ║ 14. file-chunk-info ───────────┼──────────────────────────────► ║ │ + │ ║ {fileId, chunkIndex, │ ║ │ + │ ║ totalChunks, checksum} │ ║ │ + │ ║ │ ║ │ + │ ║ 15. [ArrayBuffer 256KB] ───────┼──────────────────────────────► ║ │ + │ ║ │ ║ │ + │ ║ ◄───────────────────────────────┼── 16. file-chunk-ack ─────── ║ │ + │ ║ {fileId, chunkIndex, │ success, checksum} ║ │ + │ ║ │ ║ │ + │ ║ [如果 success=false, 指数退避重试,最多5次] ║ │ + │ ╚══════════════════════════════════╪══════════════════════════════╝ │ + │ │ │ + │ 17. file-complete ────────────────┼────────────────────────────────► │ + │ {fileId} │ │ + │ │ 18. 组装 Blob │ + │ │ → File │ + │ │ → 下载 │ +``` + +#### 4.5.4 文件列表同步机制 + +发送方选择文件后,文件列表会 **实时同步** 到接收方: + +``` +发送方文件变更 → useFileListSync hook + │ + ├─ 150ms 防抖(debounce) + │ + ├─ 比对新旧文件列表(避免无变更的冗余同步) + │ + └─ 通过 DataChannel 发送 file-list 消息 + │ + └─ 接收方更新 UI 展示文件列表 +``` + +### 4.6 文字传输详细设计 + +#### 4.6.1 DataChannel 消息协议 + +**text-transfer channel 消息类型:** + +| 方向 | type | payload | 说明 | +|------|------|---------|------| +| S→R | `text-sync` | `{ text: string }` | 实时文本内容同步 | +| 双向 | `text-typing` | `{ typing: boolean }` | 打字状态指示 | + +#### 4.6.2 实时同步机制 + +``` +发送方 Textarea 输入 + │ + ├─ onChange → handleTextInputChange() + │ + ├─ 调用 textTransfer.sendTextSync(text) + │ └─ DataChannel 发送 { channel: "text-transfer", type: "text-sync", payload: { text } } + │ + ├─ 同时发送 text-typing: { typing: true } + │ └─ 1秒后自动发送 text-typing: { typing: false } + │ + └─ 接收方 + ├─ onTextSync 回调 → 更新显示文本 + └─ onTyping 回调 → 显示 "对方正在输入..." 动画 +``` + +**图片传输**:复用文件传输的 `file-transfer` channel,图片作为文件发送。 + +### 4.7 桌面共享详细设计 + +``` +发送方 + │ + ├─ navigator.mediaDevices.getDisplayMedia({ video: true, audio: true }) + │ + ├─ 获取 MediaStream → 提取 video/audio Track + │ + ├─ trackManager.addTrack(track, stream) + │ └─ peerConnection.addTrack() + │ + ├─ trackManager.createOfferNow() ← 触发 SDP 重协商 + │ └─ 新 Offer → WebSocket → 对方 → Answer → WebSocket → 本方 + │ + └─ 接收方 + ├─ ontrack 事件 → 获取远程 MediaStream + └─