chore(doc):Update document
Update the document to include the latest changes: file continuation, introduction of Zustand to the front-end architecture.
This commit is contained in:
@@ -86,7 +86,7 @@ We have a public roadmap that outlines our vision for the future and our current
|
||||
We provide detailed documentation to help you dive deeper into the project's design and deployment details:
|
||||
|
||||
- [**Overall Project Architecture**](./docs/ARCHITECTURE.md): Understand how all components of the PrivyDrop system work together.
|
||||
- [**Frontend Architecture Deep Dive**](./docs/FRONTEND_ARCHITECTURE.md): Explore the frontend's code structure, state management, and core logic.
|
||||
- [**Frontend Architecture Deep Dive**](./docs/FRONTEND_ARCHITECTURE.md): Explore the frontend's modern, layered architecture, state management with Zustand, and the decoupled service-based approach to WebRTC.
|
||||
- [**Backend Architecture Deep Dive**](./docs/BACKEND_ARCHITECTURE.md): Dive into the backend's code structure, signaling flow, and Redis design.
|
||||
- [**Deployment Guide**](./docs/DEPLOYMENT.md): Learn how to deploy the complete PrivyDrop application in a production environment.
|
||||
|
||||
|
||||
+1
-1
@@ -86,7 +86,7 @@ PrivyDrop (原 SecureShare) 是一个基于 WebRTC 的开源点对点(P2P)
|
||||
我们提供了详尽的文档来帮助你深入了解项目的设计和部署细节:
|
||||
|
||||
- [**项目整体架构**](./docs/ARCHITECTURE.zh-CN.md): 了解 PrivyDrop 系统各个组件如何协同工作。
|
||||
- [**前端架构详解**](./docs/FRONTEND_ARCHITECTURE.zh-CN.md): 深入探索前端的代码结构、状态管理和核心逻辑。
|
||||
- [**前端架构详解**](./docs/FRONTEND_ARCHITECTURE.zh-CN.md): 深入探索前端的现代化分层架构、基于 Zustand 的状态管理,以及解耦的服务化 WebRTC 实现。
|
||||
- [**后端架构详解**](./docs/BACKEND_ARCHITECTURE.zh-CN.md): 深入探索后端的代码结构、信令流程和 Redis 设计。
|
||||
- [**部署指南**](./docs/DEPLOYMENT.zh-CN.md): 学习如何在生产环境部署完整的 PrivyDrop 应用。
|
||||
|
||||
|
||||
+12
-3
@@ -6,11 +6,20 @@ This roadmap is a living document. We welcome community feedback and contributio
|
||||
|
||||
---
|
||||
|
||||
## ✅ Completed
|
||||
|
||||
- **Core Architecture Refactor (Q3 2025)**: Successfully refactored the entire frontend codebase to a modern, layered architecture.
|
||||
- Implemented a framework-agnostic **Service Layer** (`webrtcService`) to encapsulate all WebRTC and business logic.
|
||||
- Introduced **Zustand** for centralized, predictable state management (`fileTransferStore`).
|
||||
- Decoupled UI components from business logic, establishing a clear, unidirectional data flow.
|
||||
- **Resumable File Transfers (Q3 2025):** Implemented robust logic for resuming transfers from the point of interruption. This is enabled by setting a save directory, which allows the receiver to check for partially downloaded files and request only the missing chunks.
|
||||
|
||||
---
|
||||
|
||||
## Short-Term Goals (Next 1-3 Months)
|
||||
|
||||
This phase focuses on perfecting the current feature set and enhancing reliability to build an even stronger foundation.
|
||||
|
||||
- **[High Priority] Resumable File Transfers:** Implement logic to allow file transfers to be paused and resumed. This is crucial for large files and unstable network conditions. It will involve chunk-based confirmation and state management on both peers.
|
||||
- **Enhanced Connection Stability:** The current implementation supports automatic reconnection for a short period (e.g., 15 minutes) in default 4-digit rooms. This will be extended to support custom-named rooms with a longer reconnection window (e.g., 1 hour).
|
||||
- **Detailed Transfer Error-Handling:** Provide users with clearer, more specific feedback when a transfer fails (e.g., "Peer disconnected," "Browser storage full," "Network interrupted").
|
||||
|
||||
@@ -40,7 +49,7 @@ This phase introduces powerful new features that expand PrivyDrop's use cases be
|
||||
|
||||
This section is for features that are not on the immediate roadmap but represent great opportunities for community contributions.
|
||||
|
||||
- **Comprehensive Testing:** While manual testing currently suffices, we plan to gradually introduce a testing framework (like Jest/Vitest) to improve code quality and make community contributions safer. We welcome contributions in this area.
|
||||
- **Comprehensive Testing:** The recent architectural refactor has made the codebase significantly more testable. We now plan to introduce a testing framework (like Vitest) to add unit tests for the core `webrtcService` and Zustand store, improving code quality and making community contributions safer. We welcome contributions in this area.
|
||||
- **Your Ideas Here:** Have a great idea for a feature, like screen sharing or P2P media streaming? Open an issue and let's discuss it! We believe the best ideas can come from the community.
|
||||
|
||||
## How to Contribute
|
||||
@@ -51,4 +60,4 @@ Your contributions are vital to making this roadmap a reality!
|
||||
2. **Start a Discussion:** If you're interested in a roadmap item, start a discussion to share your ideas.
|
||||
3. **Submit a PR:** Fork the repo, create a feature branch, and submit a Pull Request.
|
||||
|
||||
Thank you for being part of the PrivyDrop community! Let's build the future of private sharing, together.
|
||||
Thank you for being part of the PrivyDrop community! Let's build the future of private sharing, together.
|
||||
+12
-3
@@ -6,11 +6,20 @@
|
||||
|
||||
---
|
||||
|
||||
## ✅ 已完成
|
||||
|
||||
- **核心架构重构 (2025年Q3)**: 成功地将整个前端代码库重构为现代化的分层架构。
|
||||
- 实现了一个与框架无关的**服务层** (`webrtcService`),用于封装所有 WebRTC 和业务逻辑。
|
||||
- 引入 **Zustand** (`fileTransferStore`) 进行中心化的、可预测的状态管理。
|
||||
- 将 UI 组件与业务逻辑解耦,建立了清晰的单向数据流。
|
||||
- **文件断点续传 (2025年Q3):** 实现了稳健的断点续传逻辑。通过设置保存目录,接收方能够检查已部分下载的文件,并仅请求缺失的数据块,极大地提升了大文件和不稳定网络下的传输成功率。
|
||||
|
||||
---
|
||||
|
||||
## 短期目标 (未来 1-3 个月)
|
||||
|
||||
本阶段将专注于完善现有功能、提升核心体验的可靠性,为项目打下更坚实的基础。
|
||||
|
||||
- **[高优先级] 文件断点续传:** 实现文件传输的暂停与恢复功能。这对于大文件传输和不稳定网络环境至关重要。技术上将涉及分片确认和双端状态管理。
|
||||
- **连接稳定性增强:** 目前,使用默认 4 位数字 ID 的房间已支持短时间(例如 15 分钟)内断线重连。未来将此特性扩展到自定义名称的房间,并提供更长的重连窗口(例如 1 小时)。
|
||||
- **精细化传输错误处理:** 当传输失败时,向用户提供更清晰、具体的反馈(例如:"对方已断开连接"、"浏览器存储空间不足"、"网络中断")。
|
||||
|
||||
@@ -41,7 +50,7 @@
|
||||
|
||||
本部分用于记录那些不在当前核心规划中,但对社区贡献开放的绝佳想法。
|
||||
|
||||
- **代码测试覆盖:** 目前项目主要通过手动测试保证质量。我们计划逐步引入测试框架(如 Jest/Vitest)以提升代码质量、保障社区贡献的安全性。我们非常欢迎社区在此方面做出贡献。
|
||||
- **代码测试覆盖:** 近期的架构重构使得代码库的可测试性大大增强。我们现在计划引入测试框架(如 Vitest),为核心的 `webrtcService` 和 Zustand store 添加单元测试,以提升代码质量、保障社区贡献的安全性。我们非常欢迎社区在此方面做出贡献。
|
||||
- **你的想法:** 你是否对浏览器扩展、屏幕共享、P2P 媒体流等高级功能有很棒的想法?欢迎通过 Issue 发起讨论!我们相信最好的创意来自社区。
|
||||
|
||||
## 如何贡献
|
||||
@@ -52,4 +61,4 @@
|
||||
2. **发起讨论:** 如果你对路线图中某个项目感兴趣,欢迎发起一个讨论来分享你的想法。
|
||||
3. **提交代码:** Fork 仓库,创建你的功能分支,然后提交 Pull Request。
|
||||
|
||||
感谢你成为 PrivyDrop 社区的一员!让我们一起共创私人分享的未来。
|
||||
感谢你成为 PrivyDrop 社区的一员!让我们一起共创私人分享的未来。
|
||||
@@ -75,6 +75,7 @@ configure_nginx() {
|
||||
"$NGINX_TEMPLATE" > "$TEMP_NGINX"
|
||||
|
||||
# Copy the configuration file to the target location
|
||||
mkdir -p /etc/nginx/sites-enabled
|
||||
cp "$TEMP_NGINX" /etc/nginx/sites-enabled/default
|
||||
# cp "$TEMP_NGINX" default_temp
|
||||
rm "$TEMP_NGINX"
|
||||
|
||||
Generated
+1631
File diff suppressed because it is too large
Load Diff
+31
-25
@@ -6,13 +6,13 @@ This document provides a high-level overview of the PrivyDrop project's overall
|
||||
|
||||
The PrivyDrop system is primarily composed of the following core parts:
|
||||
|
||||
1. **Frontend**: A Single Page Application (SPA) built with Next.js. It's the interface users interact with, responsible for handling file selection, UI presentation, and initiating WebRTC connections.
|
||||
1. **Frontend**: A Single Page Application (SPA) built with Next.js. It's the interface users interact with, responsible for handling file selection and UI presentation. It features a modern, layered architecture with a clear separation between the UI (React Components), centralized state management (Zustand), and the core WebRTC business logic, which is encapsulated in a framework-agnostic service layer.
|
||||
2. **Backend**: A server built with Node.js and Express. It does not handle any file data. Its core responsibilities are:
|
||||
- **Signaling Service**: Implemented with Socket.IO, it relays signaling messages (like SDP and ICE Candidates) for the "handshake" process before a WebRTC connection is established.
|
||||
- **Room Management**: Handles the creation, joining, and status checking of rooms.
|
||||
- **API Service**: Provides auxiliary HTTP endpoints.
|
||||
3. **Redis**: An in-memory database used by the backend to store temporary data, such as room information and lists of participants, utilizing its TTL feature for automatic cleanup of expired rooms.
|
||||
4. **TURN/STUN Server (Optional)**: Used to assist WebRTC with NAT traversal, ensuring a higher success rate for P2P connections in complex network environments. STUN is used to discover public IP addresses, while TURN serves as a fallback relay server. (This feature is not enabled by default in the current setup).
|
||||
4. **TURN/STUN Server (Optional)**: Used to assist WebRTC with NAT traversal, ensuring a higher success rate for P2P connections in complex network environments. STUN is used to discover public IP addresses, while TURN serves as a fallback relay server.
|
||||
|
||||
## 2. Data Flow and Interaction Diagram
|
||||
|
||||
@@ -20,11 +20,17 @@ The following diagram illustrates the main flow for users establishing a connect
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
subgraph "User A's Browser"
|
||||
ClientA[Frontend UI]
|
||||
subgraph "User A's Browser (Frontend)"
|
||||
ClientA[UI Components] --> HooksA[Hooks Layer]
|
||||
HooksA --> StoreA[Zustand Store]
|
||||
HooksA --> ServiceA[WebRTC Service]
|
||||
ServiceA --> StoreA
|
||||
end
|
||||
subgraph "User B's Browser"
|
||||
ClientB[Frontend UI]
|
||||
subgraph "User B's Browser (Frontend)"
|
||||
ClientB[UI Components] --> HooksB[Hooks Layer]
|
||||
HooksB --> StoreB[Zustand Store]
|
||||
HooksB --> ServiceB[WebRTC Service]
|
||||
ServiceB --> StoreB
|
||||
end
|
||||
|
||||
subgraph "Server Infrastructure"
|
||||
@@ -34,40 +40,40 @@ graph TD
|
||||
TURN[TURN/STUN Server]
|
||||
end
|
||||
|
||||
ClientA -- 1. Create/Join Room (HTTP/Socket) --> Nginx
|
||||
ServiceA -- 1. Create/Join Room (HTTP/Socket) --> Nginx
|
||||
Nginx --> Backend
|
||||
Backend -- Read/Write Room Status --> Redis
|
||||
|
||||
ClientB -- 2. Join Same Room (HTTP/Socket) --> Nginx
|
||||
ServiceB -- 2. Join Same Room (HTTP/Socket) --> Nginx
|
||||
|
||||
Backend -- 3. Broadcast user join event --> ClientA
|
||||
Backend -- 3. Broadcast user join event --> ClientB
|
||||
Backend -- 3. Broadcast user join event --> ServiceA
|
||||
Backend -- 3. Broadcast user join event --> ServiceB
|
||||
|
||||
ClientA -- 4. Send Signal (Offer/ICE) --> Backend
|
||||
Backend -- 5. Forward Signal --> ClientB
|
||||
ClientB -- 6. Send Signal (Answer/ICE) --> Backend
|
||||
Backend -- 7. Forward Signal --> ClientA
|
||||
ServiceA -- 4. Send Signal (Offer/ICE) --> Backend
|
||||
Backend -- 5. Forward Signal --> ServiceB
|
||||
ServiceB -- 6. Send Signal (Answer/ICE) --> Backend
|
||||
Backend -- 7. Forward Signal --> ServiceA
|
||||
|
||||
ClientA <-.-> |8. STUN Check| TURN
|
||||
ClientB <-.-> |8. STUN Check| TURN
|
||||
ServiceA <-.-> |8. STUN Check| TURN
|
||||
ServiceB <-.-> |8. STUN Check| TURN
|
||||
|
||||
ClientA <-..- |9. P2P Direct Data Transfer| ClientB
|
||||
ClientA <-.-> |9. TURN Relayed Data Transfer| TURN
|
||||
ClientB <-.-> |9. TURN Relayed Data Transfer| TURN
|
||||
ServiceA <-..- |9. P2P Direct Data Transfer| ServiceB
|
||||
ServiceA <-.-> |9. TURN Relayed Data Transfer| TURN
|
||||
ServiceB <-.-> |9. TURN Relayed Data Transfer| TURN
|
||||
```
|
||||
|
||||
**Flow Description:**
|
||||
|
||||
1. **Room Creation/Joining**: User A (the sender) requests the backend to create a unique room ID via the frontend. The backend records this room in Redis.
|
||||
2. **Sharing & Joining**: User A shares the room ID with User B via a link or QR code. User B uses this ID to request joining the room.
|
||||
1. **Room Creation/Joining**: User A's frontend service layer requests the backend to create a unique room ID. The backend records this room in Redis.
|
||||
2. **Sharing & Joining**: User A shares the room ID with User B. User B's frontend service layer uses this ID to request joining the room.
|
||||
3. **Signaling Exchange**:
|
||||
- Once there are two or more users in a room, they begin exchanging WebRTC signaling messages through the backend's Socket.IO service.
|
||||
- This process includes exchanging network information (ICE candidates) and session descriptions (SDP offers/answers). The backend server acts merely as a "postman" for these messages, forwarding them without understanding their content.
|
||||
- Once there are two or more users in a room, their respective `WebRTC Service` instances begin exchanging signaling messages through the backend's Socket.IO service.
|
||||
- This process includes exchanging network information (ICE candidates) and session descriptions (SDP offers/answers). The backend server acts merely as a "postman" for these messages.
|
||||
4. **NAT Traversal**: The browsers use the network information obtained from the signals, along with STUN/TURN servers, to attempt and establish a direct P2P connection.
|
||||
5. **P2P Connection Established**: Once the connection is successfully established, all file and text data are transferred directly between User A's and User B's browsers, without passing through any server. If a direct connection fails, data will be relayed through a TURN server.
|
||||
5. **P2P Connection Established**: Once the connection is successfully established—which in most cases is a direct peer-to-peer link—all file and text data are transferred directly between User A's and User B's browsers via their `WebRTC Service` instances, without passing through any server. In the rare case of highly restrictive networks where a direct connection cannot be established, data will be relayed through a TURN server. Even in this relayed mode, the data remains end-to-end encrypted by WebRTC and is not visible to the TURN server.
|
||||
|
||||
## 3. Design Philosophy
|
||||
|
||||
- **Privacy First**: Core file data is never uploaded to the server. The server only acts as an "introducer" or "matchmaker."
|
||||
- **Frontend-Backend Separation**: Responsibilities are clearly separated. The frontend handles all user interaction and the complex logic of WebRTC; the backend provides lightweight, efficient signaling and room management services.
|
||||
- **Horizontal Scalability**: The backend is stateless (with state managed in Redis), which theoretically allows it to be scaled horizontally by adding more Node.js instances to handle a large volume of concurrent signaling requests.
|
||||
- **Horizontal Scalability**: The backend is stateless (with state managed in Redis), which theoretically allows it to be scaled horizontally by adding more Node.js instances to handle a large volume of concurrent signaling requests.
|
||||
+30
-24
@@ -6,13 +6,13 @@
|
||||
|
||||
PrivyDrop 系统主要由以下几个核心部分组成:
|
||||
|
||||
1. **前端 (Frontend)**: 一个使用 Next.js 构建的单页应用 (SPA)。它是用户直接交互的界面,负责处理文件选择、UI 展示、发起 WebRTC 连接等所有客户端逻辑。
|
||||
1. **前端 (Frontend)**: 一个使用 Next.js 构建的单页应用 (SPA)。它是用户直接交互的界面,负责处理文件选择和 UI 展示。其内部采用现代化的分层架构,清晰地分离了 UI (React 组件)、中心化状态管理 (Zustand) 以及封装在框架无关的服务层中的核心 WebRTC 业务逻辑。
|
||||
2. **后端 (Backend)**: 一个使用 Node.js 和 Express 构建的服务器。它不处理任何文件数据,其核心职责是:
|
||||
- **信令服务 (Signaling)**: 通过 Socket.IO 实现,为 WebRTC 连接建立前的“握手”过程传递信令消息 (如 SDP 和 ICE Candidates)。
|
||||
- **房间管理 (Room Management)**: 处理房间的创建、加入和状态检查。
|
||||
- **API 服务**: 提供一些辅助性的 HTTP 接口。
|
||||
3. **Redis**: 一个内存数据库,用于后端存储临时数据,如房间信息、房间内的用户列表等,并利用其 TTL 特性自动清理过期房间。
|
||||
4. **TURN/STUN 服务器【可选】**: 用于辅助 WebRTC 进行 NAT 穿透,确保在复杂网络环境下的 P2P 连接成功率。STUN 用于发现公网地址,TURN 则作为最后的备选方案,当中继服务器使用(目前没有使用这个特性)。
|
||||
4. **TURN/STUN 服务器【可选】**: 用于辅助 WebRTC 进行 NAT 穿透,确保在复杂网络环境下的 P2P 连接成功率。STUN 用于发现公网地址,TURN 则作为最后的备选方案,当中继服务器使用。
|
||||
|
||||
## 二、数据流与交互图
|
||||
|
||||
@@ -20,11 +20,17 @@ PrivyDrop 系统主要由以下几个核心部分组成:
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
subgraph "用户A 浏览器"
|
||||
ClientA[Frontend UI]
|
||||
subgraph "用户A 浏览器 (前端)"
|
||||
ClientA[UI 组件] --> HooksA[Hooks 层]
|
||||
HooksA --> StoreA[Zustand Store]
|
||||
HooksA --> ServiceA[WebRTC 服务]
|
||||
ServiceA --> StoreA
|
||||
end
|
||||
subgraph "用户B 浏览器"
|
||||
ClientB[Frontend UI]
|
||||
subgraph "用户B 浏览器 (前端)"
|
||||
ClientB[UI 组件] --> HooksB[Hooks 层]
|
||||
HooksB --> StoreB[Zustand Store]
|
||||
HooksB --> ServiceB[WebRTC 服务]
|
||||
ServiceB --> StoreB
|
||||
end
|
||||
|
||||
subgraph "服务器基础设施"
|
||||
@@ -34,37 +40,37 @@ graph TD
|
||||
TURN[TURN/STUN 服务器]
|
||||
end
|
||||
|
||||
ClientA -- 1. 创建/加入房间 (HTTP/Socket) --> Nginx
|
||||
ServiceA -- 1. 创建/加入房间 (HTTP/Socket) --> Nginx
|
||||
Nginx --> Backend
|
||||
Backend -- 读/写房间状态 --> Redis
|
||||
|
||||
ClientB -- 2. 加入同一房间 (HTTP/Socket) --> Nginx
|
||||
ServiceB -- 2. 加入同一房间 (HTTP/Socket) --> Nginx
|
||||
|
||||
Backend -- 3. 广播用户加入事件 --> ClientA
|
||||
Backend -- 3. 广播用户加入事件 --> ClientB
|
||||
Backend -- 3. 广播用户加入事件 --> ServiceA
|
||||
Backend -- 3. 广播用户加入事件 --> ServiceB
|
||||
|
||||
ClientA -- 4. 发送信令 (Offer/ICE) --> Backend
|
||||
Backend -- 5. 转发信令 --> ClientB
|
||||
ClientB -- 6. 发送信令 (Answer/ICE) --> Backend
|
||||
Backend -- 7. 转发信令 --> ClientA
|
||||
ServiceA -- 4. 发送信令 (Offer/ICE) --> Backend
|
||||
Backend -- 5. 转发信令 --> ServiceB
|
||||
ServiceB -- 6. 发送信令 (Answer/ICE) --> Backend
|
||||
Backend -- 7. 转发信令 --> ServiceA
|
||||
|
||||
ClientA <-.-> |8. STUN检查| TURN
|
||||
ClientB <-.-> |8. STUN检查| TURN
|
||||
ServiceA <-.-> |8. STUN检查| TURN
|
||||
ServiceB <-.-> |8. STUN检查| TURN
|
||||
|
||||
ClientA <-..- |9. P2P直连数据传输| ClientB
|
||||
ClientA <-.-> |9. TURN中继数据传输| TURN
|
||||
ClientB <-.-> |9. TURN中pey数据传输| TURN
|
||||
ServiceA <-..- |9. P2P直连数据传输| ServiceB
|
||||
ServiceA <-.-> |9. TURN中继数据传输| TURN
|
||||
ServiceB <-.-> |9. TURN中继数据传输| TURN
|
||||
```
|
||||
|
||||
**流程说明:**
|
||||
|
||||
1. **房间创建/加入**: 用户 A(发送方)通过前端请求后端创建一个唯一的房间 ID。后端在 Redis 中记录该房间。
|
||||
2. **分享与加入**: 用户 A 通过链接或二维码将房间 ID 分享给用户 B。用户 B 使用此 ID 请求加入房间。
|
||||
1. **房间创建/加入**: 用户 A 的前端服务层请求后端创建一个唯一的房间 ID。后端在 Redis 中记录该房间。
|
||||
2. **分享与加入**: 用户 A 将房间 ID 分享给用户 B。用户 B 的前端服务层使用此 ID 请求加入房间。
|
||||
3. **信令交换**:
|
||||
- 一旦房间内有两位或更多用户,他们便开始通过后端的 Socket.IO 服务交换 WebRTC 信令。
|
||||
- 这个过程包括交换网络信息 (ICE candidates) 和会话描述 (SDP offers/answers)。后端服务器仅作为这些信令消息的“邮差”,进行转发,不理解其内容。
|
||||
- 一旦房间内有两位或更多用户,他们各自的 `WebRTC Service` 实例便开始通过后端的 Socket.IO 服务交换信令。
|
||||
- 这个过程包括交换网络信息 (ICE candidates) 和会话描述 (SDP offers/answers)。后端服务器仅作为这些信令消息的“邮差”。
|
||||
4. **NAT 穿透**: 浏览器利用从信令中获取的网络信息,并借助 STUN/TURN 服务器来尝试建立直接的 P2P 连接。
|
||||
5. **P2P 连接建立**: 一旦连接建立成功,所有文件和文本数据都将直接在用户 A 和用户 B 的浏览器之间传输,不再经过任何服务器。如果直连失败,数据将通过 TURN 服务器进行中继。
|
||||
5. **P2P 连接建立**: 一旦连接成功建立——在绝大多数情况下是直接的点对点链路——所有文件和文本数据都将直接在用户 A 和用户 B 的浏览器之间通过各自的 `WebRTC Service` 实例进行传输,不再经过任何服务器。在少数网络环境极其复杂、无法建立直连的情况下,数据将通过 TURN 服务器进行中继。即便是在中继模式下,数据依然由 WebRTC 进行端到端加密,TURN 服务器无法窥探其内容。
|
||||
|
||||
## 三、设计哲学
|
||||
|
||||
|
||||
Reference in New Issue
Block a user