mirror of
https://github.com/mofeng-git/One-KVM.git
synced 2026-01-29 00:51:53 +08:00
feat: 添加 RustDesk 协议支持和项目文档
- 新增 RustDesk 模块,支持与 RustDesk 客户端连接 - 实现会合服务器协议和 P2P 连接 - 支持 NaCl 加密和密钥交换 - 添加视频帧和 HID 事件适配器 - 添加 Protobuf 协议定义 (message.proto, rendezvous.proto) - 新增完整项目文档 - 各功能模块文档 (video, hid, msd, otg, webrtc 等) - hwcodec 和 RustDesk 协议技术报告 - 系统架构和技术栈文档 - 更新 Web 前端 RustDesk 配置界面和 API
This commit is contained in:
318
docs/report/rustdesk/03-relay-protocol.md
Normal file
318
docs/report/rustdesk/03-relay-protocol.md
Normal file
@@ -0,0 +1,318 @@
|
||||
# Relay 服务器协议
|
||||
|
||||
## 概述
|
||||
|
||||
Relay Server(hbbr)是 RustDesk 的数据中转服务器,当 P2P 连接无法建立时(如双方都在 Symmetric NAT 后面),所有通信数据通过 Relay Server 转发。
|
||||
|
||||
## 服务器架构
|
||||
|
||||
### 监听端口
|
||||
|
||||
| 端口 | 协议 | 用途 |
|
||||
|------|------|------|
|
||||
| 21117 | TCP | 主要中转端口 |
|
||||
| 21119 | WebSocket | Web 客户端支持 |
|
||||
|
||||
### 核心配置
|
||||
|
||||
```rust
|
||||
// rustdesk-server/src/relay_server.rs:40-46
|
||||
static DOWNGRADE_THRESHOLD_100: AtomicUsize = AtomicUsize::new(66); // 0.66
|
||||
static DOWNGRADE_START_CHECK: AtomicUsize = AtomicUsize::new(1_800_000); // 30分钟 (ms)
|
||||
static LIMIT_SPEED: AtomicUsize = AtomicUsize::new(32 * 1024 * 1024); // 32 Mb/s
|
||||
static TOTAL_BANDWIDTH: AtomicUsize = AtomicUsize::new(1024 * 1024 * 1024);// 1024 Mb/s
|
||||
static SINGLE_BANDWIDTH: AtomicUsize = AtomicUsize::new(128 * 1024 * 1024);// 128 Mb/s
|
||||
const BLACKLIST_FILE: &str = "blacklist.txt";
|
||||
const BLOCKLIST_FILE: &str = "blocklist.txt";
|
||||
```
|
||||
|
||||
## 连接配对机制
|
||||
|
||||
### 配对原理
|
||||
|
||||
Relay Server 使用 UUID 来配对两个客户端的连接:
|
||||
|
||||
1. 第一个客户端连接并发送 `RequestRelay` 消息(包含 UUID)
|
||||
2. 服务器将该连接存储在等待队列中
|
||||
3. 第二个客户端使用相同的 UUID 连接
|
||||
4. 服务器将两个连接配对,开始转发数据
|
||||
|
||||
### 配对流程
|
||||
|
||||
```rust
|
||||
// rustdesk-server/src/relay_server.rs:425-462
|
||||
async fn make_pair_(stream: impl StreamTrait, addr: SocketAddr, key: &str, limiter: Limiter) {
|
||||
let mut stream = stream;
|
||||
if let Ok(Some(Ok(bytes))) = timeout(30_000, stream.recv()).await {
|
||||
if let Ok(msg_in) = RendezvousMessage::parse_from_bytes(&bytes) {
|
||||
if let Some(rendezvous_message::Union::RequestRelay(rf)) = msg_in.union {
|
||||
// 验证许可证密钥
|
||||
if !key.is_empty() && rf.licence_key != key {
|
||||
log::warn!("Relay authentication failed from {}", addr);
|
||||
return;
|
||||
}
|
||||
|
||||
if !rf.uuid.is_empty() {
|
||||
// 尝试查找配对
|
||||
let mut peer = PEERS.lock().await.remove(&rf.uuid);
|
||||
if let Some(peer) = peer.as_mut() {
|
||||
// 找到配对,开始中转
|
||||
log::info!("Relay request {} got paired", rf.uuid);
|
||||
relay(addr, &mut stream, peer, limiter).await;
|
||||
} else {
|
||||
// 没找到,存储等待配对
|
||||
log::info!("New relay request {} from {}", rf.uuid, addr);
|
||||
PEERS.lock().await.insert(rf.uuid.clone(), Box::new(stream));
|
||||
sleep(30.).await; // 等待 30 秒
|
||||
PEERS.lock().await.remove(&rf.uuid); // 超时移除
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 数据转发
|
||||
|
||||
### 转发逻辑
|
||||
|
||||
```rust
|
||||
// rustdesk-server/src/relay_server.rs:464-566
|
||||
async fn relay(
|
||||
addr: SocketAddr,
|
||||
stream: &mut impl StreamTrait,
|
||||
peer: &mut Box<dyn StreamTrait>,
|
||||
total_limiter: Limiter,
|
||||
) -> ResultType<()> {
|
||||
let limiter = <Limiter>::new(SINGLE_BANDWIDTH.load(Ordering::SeqCst) as f64);
|
||||
let blacklist_limiter = <Limiter>::new(LIMIT_SPEED.load(Ordering::SeqCst) as _);
|
||||
|
||||
loop {
|
||||
tokio::select! {
|
||||
// 从 peer 接收数据,发送给 stream
|
||||
res = peer.recv() => {
|
||||
if let Some(Ok(bytes)) = res {
|
||||
// 带宽限制
|
||||
if blacked || downgrade {
|
||||
blacklist_limiter.consume(bytes.len() * 8).await;
|
||||
} else {
|
||||
limiter.consume(bytes.len() * 8).await;
|
||||
}
|
||||
total_limiter.consume(bytes.len() * 8).await;
|
||||
stream.send_raw(bytes.into()).await?;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
},
|
||||
// 从 stream 接收数据,发送给 peer
|
||||
res = stream.recv() => {
|
||||
if let Some(Ok(bytes)) = res {
|
||||
// 带宽限制
|
||||
limiter.consume(bytes.len() * 8).await;
|
||||
total_limiter.consume(bytes.len() * 8).await;
|
||||
peer.send_raw(bytes.into()).await?;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
},
|
||||
_ = timer.tick() => {
|
||||
// 超时检测
|
||||
if last_recv_time.elapsed().as_secs() > 30 {
|
||||
bail!("Timeout");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 降级检测
|
||||
if elapsed > DOWNGRADE_START_CHECK && total > elapsed * downgrade_threshold {
|
||||
downgrade = true;
|
||||
log::info!("Downgrade {}, exceed threshold", id);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
```
|
||||
|
||||
### 原始模式
|
||||
|
||||
当两端都支持原始模式时,跳过 protobuf 解析以提高性能:
|
||||
|
||||
```rust
|
||||
// rustdesk-server/src/relay_server.rs:440-444
|
||||
if !stream.is_ws() && !peer.is_ws() {
|
||||
peer.set_raw();
|
||||
stream.set_raw();
|
||||
log::info!("Both are raw");
|
||||
}
|
||||
```
|
||||
|
||||
## 带宽控制
|
||||
|
||||
### 多级限速
|
||||
|
||||
1. **总带宽限制**:整个服务器的总带宽
|
||||
2. **单连接限制**:每个中转连接的带宽
|
||||
3. **黑名单限速**:对黑名单 IP 的特殊限制
|
||||
|
||||
### 降级机制
|
||||
|
||||
当连接持续占用高带宽时,会触发降级:
|
||||
|
||||
```rust
|
||||
// 条件:
|
||||
// 1. 连接时间 > DOWNGRADE_START_CHECK (30分钟)
|
||||
// 2. 平均带宽 > SINGLE_BANDWIDTH * 0.66
|
||||
// 降级后使用 LIMIT_SPEED (32 Mb/s) 限速
|
||||
if elapsed > DOWNGRADE_START_CHECK.load(Ordering::SeqCst)
|
||||
&& !downgrade
|
||||
&& total > elapsed * downgrade_threshold
|
||||
{
|
||||
downgrade = true;
|
||||
}
|
||||
```
|
||||
|
||||
## 安全控制
|
||||
|
||||
### 黑名单
|
||||
|
||||
用于限速特定 IP:
|
||||
|
||||
```
|
||||
# blacklist.txt
|
||||
192.168.1.100
|
||||
10.0.0.50
|
||||
```
|
||||
|
||||
### 封锁名单
|
||||
|
||||
用于完全拒绝特定 IP:
|
||||
|
||||
```
|
||||
# blocklist.txt
|
||||
1.2.3.4
|
||||
5.6.7.8
|
||||
```
|
||||
|
||||
### 运行时管理命令
|
||||
|
||||
通过本地 TCP 连接(仅限 localhost)发送命令:
|
||||
|
||||
```rust
|
||||
// rustdesk-server/src/relay_server.rs:152-324
|
||||
match fds.next() {
|
||||
Some("h") => // 帮助
|
||||
Some("blacklist-add" | "ba") => // 添加黑名单
|
||||
Some("blacklist-remove" | "br") => // 移除黑名单
|
||||
Some("blacklist" | "b") => // 查看黑名单
|
||||
Some("blocklist-add" | "Ba") => // 添加封锁名单
|
||||
Some("blocklist-remove" | "Br") => // 移除封锁名单
|
||||
Some("blocklist" | "B") => // 查看封锁名单
|
||||
Some("downgrade-threshold" | "dt") => // 设置降级阈值
|
||||
Some("downgrade-start-check" | "t") => // 设置降级检测时间
|
||||
Some("limit-speed" | "ls") => // 设置限速
|
||||
Some("total-bandwidth" | "tb") => // 设置总带宽
|
||||
Some("single-bandwidth" | "sb") => // 设置单连接带宽
|
||||
Some("usage" | "u") => // 查看使用统计
|
||||
}
|
||||
```
|
||||
|
||||
## 协议消息
|
||||
|
||||
### RequestRelay
|
||||
|
||||
用于建立中转连接的请求消息:
|
||||
|
||||
```protobuf
|
||||
message RequestRelay {
|
||||
string id = 1; // 目标 Peer ID
|
||||
string uuid = 2; // 连接 UUID(配对用)
|
||||
bytes socket_addr = 3; // 本端地址
|
||||
string relay_server = 4; // Relay 服务器
|
||||
bool secure = 5; // 是否加密
|
||||
string licence_key = 6; // 许可证密钥
|
||||
ConnType conn_type = 7; // 连接类型
|
||||
string token = 8; // 认证令牌
|
||||
}
|
||||
```
|
||||
|
||||
## 时序图
|
||||
|
||||
### 中转连接建立
|
||||
|
||||
```
|
||||
客户端 A Relay Server 客户端 B
|
||||
│ │ │
|
||||
│ RequestRelay(uuid) │ │
|
||||
├─────────────────────────►│ │
|
||||
│ │ │
|
||||
│ │ (存储等待配对) │
|
||||
│ │ │
|
||||
│ │ RequestRelay(uuid) │
|
||||
│ │◄───────────────────────────┤
|
||||
│ │ │
|
||||
│ │ (配对成功) │
|
||||
│ │ │
|
||||
│ ◄────────── 数据转发 ─────────────────────────────────►│
|
||||
│ │ │
|
||||
```
|
||||
|
||||
### 数据转发
|
||||
|
||||
```
|
||||
客户端 A Relay Server 客户端 B
|
||||
│ │ │
|
||||
│ ────[数据]───────► │ │
|
||||
│ │ ────[数据]───────► │
|
||||
│ │ │
|
||||
│ │ ◄───[数据]──────── │
|
||||
│ ◄───[数据]──────── │ │
|
||||
│ │ │
|
||||
```
|
||||
|
||||
## 性能优化
|
||||
|
||||
### 零拷贝
|
||||
|
||||
使用 `Bytes` 类型减少内存拷贝:
|
||||
|
||||
```rust
|
||||
async fn send_raw(&mut self, bytes: Bytes) -> ResultType<()>;
|
||||
```
|
||||
|
||||
### WebSocket 支持
|
||||
|
||||
支持 WebSocket 协议以穿越防火墙:
|
||||
|
||||
```rust
|
||||
#[async_trait]
|
||||
impl StreamTrait for tokio_tungstenite::WebSocketStream<TcpStream> {
|
||||
async fn recv(&mut self) -> Option<Result<BytesMut, Error>> {
|
||||
if let Some(msg) = self.next().await {
|
||||
match msg {
|
||||
Ok(tungstenite::Message::Binary(bytes)) => {
|
||||
Some(Ok(bytes[..].into()))
|
||||
}
|
||||
// ...
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 监控指标
|
||||
|
||||
服务器跟踪以下指标:
|
||||
|
||||
| 指标 | 说明 |
|
||||
|------|------|
|
||||
| elapsed | 连接持续时间 (ms) |
|
||||
| total | 总传输数据量 (bit) |
|
||||
| highest | 最高瞬时速率 (kb/s) |
|
||||
| speed | 当前速率 (kb/s) |
|
||||
|
||||
通过 `usage` 命令查看:
|
||||
|
||||
```
|
||||
192.168.1.100:12345: 3600s 1024.00MB 50000kb/s 45000kb/s 42000kb/s
|
||||
```
|
||||
Reference in New Issue
Block a user