You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用russh创建支持多连接的SSH隧道?

使用russh实现SSH隧道多连接支持

场景与现有代码

我正在用Rust和russh crate实现SSH隧道,通过本地监听端口访问远程服务器,当前代码如下:

let mut ssh_client= russh::client::connect(
    Arc::new(Config::default()),
    format!("{}:{}", self.host, self.port),
    IHandler {},
).await?;

ssh_client.authenticate_password(self.username.clone(), self.password.clone()).await?;

let listener = TcpListener::bind(SocketAddrV4::new(Ipv4Addr::LOCALHOST, 0)).await?;
let addr = listener.local_addr()?;

let channel = ssh_client.channel_open_direct_tcpip(
    self.forwarding_host.clone(),
    self.forwarding_port as u32,
    Ipv4Addr::LOCALHOST.to_string(),
    addr.port() as u32,
).await?;

let mut remote_stream = channel.into_stream();
tokio::spawn(async move {
    loop {
        if let Ok((mut local_stream, _)) = listener.accept().await {
            tokio::spawn(async move {
                select! {
                    result = tokio::io::copy_bidirectional_with_sizes(&mut local_stream, &mut remote_stream, 255, 8 * 1024) => {
                        // close 
                }
            });
        }
        if rx_clone.changed().await.is_ok() {
            break;
        }
    }
    drop(listener);
    Ok::<(), Error>(())
});

现有问题

代码能成功建立SSH隧道,但本地监听端口仅支持一个连接,后续连接尝试会被阻塞,需要实现多连接支持。

环境

  • russh = "0.45.0"
  • tokio = "1"

注:单个SSH隧道本身支持多连接。


解决方案

问题出在你复用了同一个SSH通道流来处理所有本地连接。每个本地连接都需要单独向SSH服务器请求新的direct-tcpip通道,而不是共用一个通道流。

修改思路:

  1. 把ssh_client包装成Arc,方便在子任务中克隆使用(russh的Client内部基于原子引用计数,支持安全克隆)
  2. 移除全局的remote_stream,改为在每次接收到本地连接时,新建一个SSH通道并转换为流

修改后的代码示例:

// 将客户端包装为Arc,方便多任务克隆
let ssh_client = Arc::new(ssh_client);

let listener = TcpListener::bind(SocketAddrV4::new(Ipv4Addr::LOCALHOST, 0)).await?;
let addr = listener.local_addr()?;

// 提前克隆转发配置,避免循环内重复克隆
let forwarding_host = self.forwarding_host.clone();
let forwarding_port = self.forwarding_port as u32;

tokio::spawn(async move {
    loop {
        tokio::select! {
            // 优先处理新的本地连接请求
            accept_result = listener.accept() => {
                if let Ok((mut local_stream, _)) = accept_result {
                    // 克隆客户端和转发配置到子任务
                    let client = ssh_client.clone();
                    let host = forwarding_host.clone();
                    let port = forwarding_port;
                    let local_port = addr.port() as u32;

                    tokio::spawn(async move {
                        // 为每个本地连接创建独立的SSH通道
                        match client.channel_open_direct_tcpip(
                            host,
                            port,
                            Ipv4Addr::LOCALHOST.to_string(),
                            local_port,
                        ).await {
                            Ok(channel) => {
                                let mut remote_stream = channel.into_stream();
                                // 双向拷贝数据,完成后自动关闭流与通道
                                let _ = tokio::io::copy_bidirectional_with_sizes(&mut local_stream, &mut remote_stream, 255, 8 * 1024).await;
                            }
                            Err(e) => {
                                eprintln!("Failed to open SSH channel: {}", e);
                            }
                        }
                    });
                }
            }
            // 处理停止信号
            _ = rx_clone.changed() => {
                break;
            }
        }
    }
    drop(listener);
    Ok::<(), Error>(())
});

关键说明:

  • russh::client::Client实现了Clone trait,基于内部Arc可以安全地在多个异步任务中共享
  • 每个本地连接对应独立的SSH通道,避免了单通道的阻塞问题,实现多连接并行处理
  • 使用tokio::select!替代顺序判断,让连接处理与停止信号监听的逻辑更高效

内容的提问来源于stack exchange,提问作者honhim

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 17:30:55