Files
One-KVM/docs/modules/rustdesk.md
mofeng-git a8a3b6c66b feat: 添加 RustDesk 协议支持和项目文档
- 新增 RustDesk 模块,支持与 RustDesk 客户端连接
  - 实现会合服务器协议和 P2P 连接
  - 支持 NaCl 加密和密钥交换
  - 添加视频帧和 HID 事件适配器
- 添加 Protobuf 协议定义 (message.proto, rendezvous.proto)
- 新增完整项目文档
  - 各功能模块文档 (video, hid, msd, otg, webrtc 等)
  - hwcodec 和 RustDesk 协议技术报告
  - 系统架构和技术栈文档
- 更新 Web 前端 RustDesk 配置界面和 API
2025-12-31 18:59:52 +08:00

20 KiB

RustDesk 模块文档

1. 模块概述

RustDesk 模块实现 RustDesk 协议集成,允许使用标准 RustDesk 客户端访问 One-KVM 设备。

1.1 主要功能

  • RustDesk 协议实现
  • 渲染服务器 (hbbs) 通信
  • 中继服务器 (hbbr) 通信
  • 视频/音频/HID 转换
  • 端到端加密

1.2 文件结构

src/rustdesk/
├── mod.rs              # RustDeskService (21KB)
├── connection.rs       # 连接管理 (49KB)
├── rendezvous.rs       # 渲染服务器 (32KB)
├── crypto.rs           # NaCl 加密 (16KB)
├── config.rs           # 配置 (7KB)
├── hid_adapter.rs      # HID 适配 (14KB)
├── frame_adapters.rs   # 帧转换 (9KB)
├── protocol.rs         # 协议包装 (6KB)
└── bytes_codec.rs      # 帧编码 (8KB)

2. 架构设计

2.1 RustDesk 网络架构

┌─────────────────────────────────────────────────────────────────────────────┐
│                      RustDesk Network Architecture                           │
└─────────────────────────────────────────────────────────────────────────────┘

┌─────────────┐                                      ┌─────────────┐
│  RustDesk   │                                      │  One-KVM    │
│   Client    │                                      │   Device    │
└──────┬──────┘                                      └──────┬──────┘
       │                                                    │
       │ 1. 查询设备地址                                     │
       │─────────────────────►┌─────────────┐◄──────────────│
       │                      │    hbbs     │               │
       │                      │ (Rendezvous)│               │
       │◄─────────────────────└─────────────┘               │
       │ 2. 返回地址                                         │
       │                                                    │
       │ 3a. 直接连接 (如果可达)                              │
       │────────────────────────────────────────────────────│
       │                                                    │
       │ 3b. 中继连接 (如果 NAT)                              │
       │─────────────────────►┌─────────────┐◄──────────────│
       │                      │    hbbr     │               │
       │                      │  (Relay)    │               │
       │◄─────────────────────└─────────────┘───────────────│
       │                                                    │
       │ 4. 建立加密通道                                     │
       │◄───────────────────────────────────────────────────│
       │                                                    │
       │ 5. 传输视频/音频/HID                                │
       │◄───────────────────────────────────────────────────│

2.2 模块内部架构

┌─────────────────────────────────────────────────────────────────────────────┐
│                      RustDesk Module Architecture                            │
└─────────────────────────────────────────────────────────────────────────────┘

                    ┌─────────────────┐
                    │ RustDeskService │
                    │    (mod.rs)     │
                    └────────┬────────┘
                             │
         ┌───────────────────┼───────────────────┐
         │                   │                   │
         ▼                   ▼                   ▼
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│  Rendezvous     │ │  Connection     │ │   Crypto        │
│  (rendezvous)   │ │  (connection)   │ │   (crypto)      │
└────────┬────────┘ └────────┬────────┘ └─────────────────┘
         │                   │
         │                   │
         ▼                   ▼
┌─────────────────┐ ┌─────────────────────────────────────┐
│  hbbs Server    │ │              Adapters               │
│  Connection     │ │  ┌──────────┐ ┌──────────────────┐ │
└─────────────────┘ │  │ HID      │ │ Frame            │ │
                    │  │ Adapter  │ │ Adapters         │ │
                    │  └──────────┘ └──────────────────┘ │
                    └─────────────────────────────────────┘
                                      │
                    ┌─────────────────┼─────────────────┐
                    │                 │                 │
                    ▼                 ▼                 ▼
             ┌───────────┐    ┌───────────┐    ┌───────────┐
             │    HID    │    │   Video   │    │   Audio   │
             │ Controller│    │  Pipeline │    │ Pipeline  │
             └───────────┘    └───────────┘    └───────────┘

3. 核心组件

3.1 RustDeskService (mod.rs)

RustDesk 服务主类。

pub struct RustDeskService {
    /// 服务配置
    config: Arc<RwLock<RustDeskConfig>>,

    /// 渲染连接
    rendezvous: Arc<RwLock<Option<RendezvousConnection>>>,

    /// 客户端连接
    connections: Arc<RwLock<HashMap<String, Arc<ClientConnection>>>>,

    /// 加密密钥
    keys: Arc<RustDeskKeys>,

    /// 视频管道
    video_pipeline: Arc<SharedVideoPipeline>,

    /// 音频管道
    audio_pipeline: Arc<SharedAudioPipeline>,

    /// HID 控制器
    hid: Arc<HidController>,

    /// 服务状态
    status: Arc<RwLock<ServiceStatus>>,

    /// 事件总线
    events: Arc<EventBus>,
}

impl RustDeskService {
    /// 创建服务
    pub async fn new(
        config: RustDeskConfig,
        video_pipeline: Arc<SharedVideoPipeline>,
        audio_pipeline: Arc<SharedAudioPipeline>,
        hid: Arc<HidController>,
        events: Arc<EventBus>,
    ) -> Result<Arc<Self>>;

    /// 启动服务
    pub async fn start(&self) -> Result<()>;

    /// 停止服务
    pub async fn stop(&self) -> Result<()>;

    /// 获取设备 ID
    pub fn device_id(&self) -> String;

    /// 获取状态
    pub fn status(&self) -> ServiceStatus;

    /// 更新配置
    pub async fn update_config(&self, config: RustDeskConfig) -> Result<()>;

    /// 获取连接列表
    pub fn connections(&self) -> Vec<ConnectionInfo>;

    /// 断开连接
    pub async fn disconnect(&self, connection_id: &str) -> Result<()>;
}

pub enum ServiceStatus {
    Stopped,
    Starting,
    Running,
    Error(String),
}

pub struct ConnectionInfo {
    pub id: String,
    pub peer_id: String,
    pub connected_at: DateTime<Utc>,
    pub ip: String,
}

3.2 RendezvousConnection (rendezvous.rs)

渲染服务器连接管理。

pub struct RendezvousConnection {
    /// 服务器地址
    server_addr: SocketAddr,

    /// TCP 连接
    stream: TcpStream,

    /// 设备 ID
    device_id: String,

    /// 公钥
    public_key: [u8; 32],

    /// 注册状态
    registered: AtomicBool,

    /// 心跳任务
    heartbeat_task: Option<JoinHandle<()>>,
}

impl RendezvousConnection {
    /// 连接到渲染服务器
    pub async fn connect(
        server: &str,
        device_id: &str,
        keys: &RustDeskKeys,
    ) -> Result<Self>;

    /// 注册设备
    pub async fn register(&self) -> Result<()>;

    /// 发送心跳
    async fn heartbeat(&self) -> Result<()>;

    /// 接收消息
    pub async fn recv_message(&mut self) -> Result<RendezvousMessage>;

    /// 处理穿孔请求
    pub async fn handle_punch_request(&self, peer_id: &str) -> Result<SocketAddr>;
}

pub enum RendezvousMessage {
    RegisterOk,
    PunchRequest { peer_id: String, socket_addr: SocketAddr },
    Heartbeat,
    Error(String),
}

3.3 ClientConnection (connection.rs)

客户端连接处理。

pub struct ClientConnection {
    /// 连接 ID
    id: String,

    /// 对端 ID
    peer_id: String,

    /// 加密通道
    channel: EncryptedChannel,

    /// 帧适配器
    frame_adapter: FrameAdapter,

    /// HID 适配器
    hid_adapter: HidAdapter,

    /// 状态
    state: Arc<RwLock<ConnectionState>>,
}

impl ClientConnection {
    /// 创建连接
    pub async fn new(
        stream: TcpStream,
        keys: &RustDeskKeys,
        peer_public_key: &[u8],
    ) -> Result<Self>;

    /// 处理连接
    pub async fn handle(
        &self,
        video_rx: broadcast::Receiver<EncodedFrame>,
        audio_rx: broadcast::Receiver<AudioFrame>,
        hid: Arc<HidController>,
    ) -> Result<()>;

    /// 发送视频帧
    async fn send_video_frame(&self, frame: &EncodedFrame) -> Result<()>;

    /// 发送音频帧
    async fn send_audio_frame(&self, frame: &AudioFrame) -> Result<()>;

    /// 处理输入事件
    async fn handle_input(&self, msg: &InputMessage) -> Result<()>;

    /// 关闭连接
    pub async fn close(&self) -> Result<()>;
}

pub enum ConnectionState {
    Handshaking,
    Authenticating,
    Connected,
    Closing,
    Closed,
}

3.4 RustDeskKeys (crypto.rs)

加密密钥管理。

pub struct RustDeskKeys {
    /// 设备 ID
    pub device_id: String,

    /// Curve25519 公钥
    pub public_key: [u8; 32],

    /// Curve25519 私钥
    secret_key: [u8; 32],

    /// Ed25519 签名公钥
    pub sign_public_key: [u8; 32],

    /// Ed25519 签名私钥
    sign_secret_key: [u8; 64],
}

impl RustDeskKeys {
    /// 生成新密钥
    pub fn generate() -> Self;

    /// 从配置加载
    pub fn from_config(config: &KeyConfig) -> Result<Self>;

    /// 保存到配置
    pub fn to_config(&self) -> KeyConfig;

    /// 计算共享密钥
    pub fn shared_secret(&self, peer_public_key: &[u8; 32]) -> [u8; 32];

    /// 签名消息
    pub fn sign(&self, message: &[u8]) -> [u8; 64];

    /// 验证签名
    pub fn verify(public_key: &[u8; 32], message: &[u8], signature: &[u8; 64]) -> bool;
}

pub struct EncryptedChannel {
    /// 发送密钥
    send_key: [u8; 32],

    /// 接收密钥
    recv_key: [u8; 32],

    /// 发送 nonce
    send_nonce: AtomicU64,

    /// 接收 nonce
    recv_nonce: AtomicU64,
}

impl EncryptedChannel {
    /// 加密消息
    pub fn encrypt(&self, plaintext: &[u8]) -> Vec<u8>;

    /// 解密消息
    pub fn decrypt(&self, ciphertext: &[u8]) -> Result<Vec<u8>>;
}

3.5 HidAdapter (hid_adapter.rs)

RustDesk HID 事件转换。

pub struct HidAdapter {
    hid: Arc<HidController>,
}

impl HidAdapter {
    /// 创建适配器
    pub fn new(hid: Arc<HidController>) -> Self;

    /// 处理键盘事件
    pub async fn handle_keyboard(&self, event: &RdKeyboardEvent) -> Result<()>;

    /// 处理鼠标事件
    pub async fn handle_mouse(&self, event: &RdMouseEvent) -> Result<()>;

    /// 转换键码
    fn convert_keycode(rd_key: u32) -> Option<KeyCode>;

    /// 转换鼠标按钮
    fn convert_button(rd_button: u32) -> Option<MouseButton>;
}

/// RustDesk 键盘事件
pub struct RdKeyboardEvent {
    pub keycode: u32,
    pub down: bool,
    pub modifiers: u32,
}

/// RustDesk 鼠标事件
pub struct RdMouseEvent {
    pub x: i32,
    pub y: i32,
    pub mask: u32,
}

3.6 FrameAdapter (frame_adapters.rs)

帧格式转换。

pub struct FrameAdapter;

impl FrameAdapter {
    /// 转换视频帧到 RustDesk 格式
    pub fn to_rd_video_frame(frame: &EncodedFrame) -> RdVideoFrame;

    /// 转换音频帧到 RustDesk 格式
    pub fn to_rd_audio_frame(frame: &AudioFrame) -> RdAudioFrame;
}

/// RustDesk 视频帧
pub struct RdVideoFrame {
    pub data: Vec<u8>,
    pub key_frame: bool,
    pub pts: i64,
    pub format: RdVideoFormat,
}

pub enum RdVideoFormat {
    H264,
    H265,
    VP8,
    VP9,
}

/// RustDesk 音频帧
pub struct RdAudioFrame {
    pub data: Vec<u8>,
    pub timestamp: u64,
}

3.7 协议消息 (protocol.rs)

Protobuf 消息包装。

/// 使用 prost 生成的 protobuf 消息
pub mod proto {
    include!(concat!(env!("OUT_DIR"), "/rendezvous.rs"));
    include!(concat!(env!("OUT_DIR"), "/message.rs"));
}

pub struct MessageCodec;

impl MessageCodec {
    /// 编码消息
    pub fn encode<M: prost::Message>(msg: &M) -> Vec<u8>;

    /// 解码消息
    pub fn decode<M: prost::Message + Default>(data: &[u8]) -> Result<M>;
}

3.8 帧编码 (bytes_codec.rs)

变长帧协议。

pub struct BytesCodec {
    state: DecodeState,
    buffer: BytesMut,
}

impl BytesCodec {
    /// 编码帧
    pub fn encode_frame(data: &[u8]) -> Vec<u8> {
        let mut buf = Vec::with_capacity(4 + data.len());
        buf.extend_from_slice(&(data.len() as u32).to_be_bytes());
        buf.extend_from_slice(data);
        buf
    }

    /// 解码帧
    pub fn decode_frame(&mut self, src: &mut BytesMut) -> Result<Option<Bytes>>;
}

enum DecodeState {
    Length,
    Data(usize),
}

4. 协议详解

4.1 Protobuf 定义

// protos/rendezvous.proto
message RegisterPeer {
    string id = 1;
    bytes public_key = 2;
}

message RegisterPeerResponse {
    bool ok = 1;
    string error = 2;
}

message PunchHoleRequest {
    string id = 1;
    string nat_type = 2;
}

// protos/message.proto
message VideoFrame {
    bytes data = 1;
    bool key = 2;
    int64 pts = 3;
    VideoCodec codec = 4;
}

message AudioFrame {
    bytes data = 1;
    int64 timestamp = 2;
}

message KeyboardEvent {
    uint32 keycode = 1;
    bool down = 2;
    uint32 modifiers = 3;
}

message MouseEvent {
    int32 x = 1;
    int32 y = 2;
    uint32 mask = 3;
}

4.2 连接握手

1. TCP 连接
   Client ────► Device

2. 公钥交换
   Client ◄───► Device

3. DH 密钥协商
   shared_secret = X25519(my_private, peer_public)

4. 密钥派生
   send_key = HKDF(shared_secret, "send")
   recv_key = HKDF(shared_secret, "recv")

5. 认证 (可选)
   Client ────► Device: encrypted(password)
   Client ◄──── Device: encrypted(ok/fail)

6. 开始传输

5. 配置

#[derive(Serialize, Deserialize)]
#[typeshare]
pub struct RustDeskConfig {
    /// 是否启用
    pub enabled: bool,

    /// 渲染服务器地址
    pub rendezvous_server: String,

    /// 中继服务器地址
    pub relay_server: Option<String>,

    /// 设备 ID (自动生成)
    pub device_id: Option<String>,

    /// 访问密码
    pub password: Option<String>,

    /// 允许的客户端 ID
    pub allowed_clients: Vec<String>,
}

impl Default for RustDeskConfig {
    fn default() -> Self {
        Self {
            enabled: false,
            rendezvous_server: "rs-ny.rustdesk.com:21116".to_string(),
            relay_server: None,
            device_id: None,
            password: None,
            allowed_clients: vec![],
        }
    }
}

6. API 端点

端点 方法 描述
/api/rustdesk/status GET 获取服务状态
/api/rustdesk/start POST 启动服务
/api/rustdesk/stop POST 停止服务
/api/rustdesk/config GET 获取配置
/api/rustdesk/config PATCH 更新配置
/api/rustdesk/device-id GET 获取设备 ID
/api/rustdesk/connections GET 获取连接列表
/api/rustdesk/connections/:id DELETE 断开连接

响应格式

// GET /api/rustdesk/status
{
    "status": "running",
    "device_id": "123456789",
    "rendezvous_connected": true,
    "active_connections": 1
}

// GET /api/rustdesk/connections
{
    "connections": [
        {
            "id": "conn-abc",
            "peer_id": "987654321",
            "connected_at": "2024-01-15T10:30:00Z",
            "ip": "192.168.1.100"
        }
    ]
}

7. 事件

pub enum SystemEvent {
    RustDeskStatusChanged {
        status: String,
        device_id: Option<String>,
        error: Option<String>,
    },

    RustDeskConnectionOpened {
        connection_id: String,
        peer_id: String,
    },

    RustDeskConnectionClosed {
        connection_id: String,
        peer_id: String,
        reason: String,
    },
}

8. 错误处理

#[derive(Debug, thiserror::Error)]
pub enum RustDeskError {
    #[error("Service not running")]
    NotRunning,

    #[error("Already running")]
    AlreadyRunning,

    #[error("Rendezvous connection failed: {0}")]
    RendezvousFailed(String),

    #[error("Authentication failed")]
    AuthFailed,

    #[error("Connection refused")]
    ConnectionRefused,

    #[error("Encryption error: {0}")]
    EncryptionError(String),

    #[error("Protocol error: {0}")]
    ProtocolError(String),

    #[error("Timeout")]
    Timeout,
}

9. 使用示例

9.1 启动服务

let config = RustDeskConfig {
    enabled: true,
    rendezvous_server: "rs-ny.rustdesk.com:21116".to_string(),
    password: Some("mypassword".to_string()),
    ..Default::default()
};

let service = RustDeskService::new(
    config,
    video_pipeline,
    audio_pipeline,
    hid,
    events,
).await?;

service.start().await?;

println!("Device ID: {}", service.device_id());

9.2 客户端连接

1. 打开 RustDesk 客户端
2. 输入设备 ID
3. 输入密码 (如果设置)
4. 连接成功后即可控制

10. 常见问题

Q: 无法连接到渲染服务器?

  1. 检查网络连接
  2. 检查服务器地址
  3. 检查防火墙

Q: 客户端连接失败?

  1. 检查设备 ID
  2. 检查密码
  3. 检查 NAT 穿透

Q: 视频延迟高?

  1. 使用更近的中继服务器
  2. 检查网络带宽
  3. 降低视频质量

Q: 如何自建服务器?

参考 RustDesk Server 部署文档:

  • hbbs: 渲染服务器
  • hbbr: 中继服务器