跳转至

双向代理架构

实现状态:✅ 已实现

双向代理架构已完全实现并在生产环境中运行。

架构概览

客户端 ←→ WebSocket ←→ 服务器 ←→ 目标服务器 (TCP)
         ↖_________________________↗
              双向流量

数据流

  1. 客户端发送 ProxyConnect → 服务器连接目标 → 服务器返回 ProxyResponse
  2. 客户端发送 ProxyData → 服务器写入目标
  3. 服务器从目标读取 → 服务器加密 ProxyData → 服务器发送给客户端
  4. 正确处理来自 WebSocket 和目标 TCP 流的并发读取

技术实现

1. Ratchet 并发访问

DoubleRatchet 被包装在 Arc<RwLock<DoubleRatchet>> 中以实现线程安全访问: - WebSocket 读取器任务:解密客户端消息 - 目标读取器任务:加密目标响应

两个操作现在可以使用 SplitRatchet 实现并发进行。

2. 目标读取器任务

对于每个代理连接,有一个专用任务从目标读取:

async fn target_reader(
    connection_id: u64,
    mut target_stream: TcpStream,
    ratchet: Arc<RwLock<DoubleRatchet>>,
    mut ws_write: impl SinkExt<Message>,
) {
    let mut buf = [0u8; 8192];
    loop {
        match target_stream.read(&mut buf).await {
            Ok(0) => break, // EOF
            Ok(n) => {
                let data = &buf[..n];
                let proxy_data = ProxyData {
                    connection_id,
                    data: data.to_vec(),
                    close: false,
                };
                // 加密并通过 WebSocket 发送
                let encrypted = {
                    let mut ratchet_guard = ratchet.write().await;
                    ratchet_guard.encrypt(&msg_bytes, PayloadType::ProxyData as u8)
                };
                ws_write.send(Message::Binary(encrypted)).await.ok();
            }
            Err(_) => break,
        }
    }
}

3. 连接生命周期管理

每个代理连接维护: - 连接 ID:用于多路复用的唯一标识符 - 目标 TCP 流:到目标服务器的连接 - 目标读取器任务:读取目标响应的异步任务 - 断开时清理:双向正确清理资源

4. 连接池

服务器实现了连接池以高效复用资源: - 尽可能复用已建立的连接 - 正确关闭空闲连接 - 高效处理并发连接

修改的文件

  1. rvpn-server/src/handler.rs
  2. 使用 Arc<RwLock<DoubleRatchet>>handle_vpn_traffic
  3. handle_proxy_connect 生成目标读取器任务
  4. 用于读取目标响应的 target_reader 函数
  5. 连接清理逻辑

  6. rvpn-core/src/crypto/ratchet.rs

  7. DoubleRatchetSend 以支持异步使用
  8. SplitRatchet 用于并发加密/解密

测试结果

  • ✅ 基本 HTTP 请求/响应
  • ✅ HTTPS 请求/响应
  • ✅ 大文件下载
  • ✅ 并发多连接
  • ✅ 连接超时处理
  • ✅ 优雅关闭
  • ✅ 错误传播到客户端