如何用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通道,而不是共用一个通道流。
修改思路:
- 把
ssh_client包装成Arc,方便在子任务中克隆使用(russh的Client内部基于原子引用计数,支持安全克隆) - 移除全局的
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实现了Clonetrait,基于内部Arc可以安全地在多个异步任务中共享- 每个本地连接对应独立的SSH通道,避免了单通道的阻塞问题,实现多连接并行处理
- 使用
tokio::select!替代顺序判断,让连接处理与停止信号监听的逻辑更高效
内容的提问来源于stack exchange,提问作者honhim
相关产品推荐
相关产品推荐

