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

Rust双向TCP代理中TcpStream转发阻塞的正确实现方案

问题原因及底层原理

阻塞的根因和TCP全双工特性、std::io::copy的执行逻辑直接相关:

  • std::io::copy 会持续从读端读取数据写入写端,直到读端返回EOF才会结束返回
  • TCP是全双工协议,两个方向的数据流独立管理:默认情况下只要没有主动关闭连接的写方向,对端的读操作就会一直阻塞等待数据,不会收到EOF

注释掉shutdown时的执行流程如下:

  1. 后端服务主动关闭了到代理的连接写方向,dst2src线程的std::io::copy从destination读端拿到EOF,正常返回
  2. 但此时代理到客户端的source连接的写方向没有被关闭,客户端不知道后端已经没有数据返回,可能还保持着连接的写方向等待后续响应
  3. 这时候src2dst线程还在从source读端等待客户端发新数据,永远拿不到EOF,线程一直阻塞,后续src2dst.join()就会一直卡住

当前实现代理HTTP流量暂时没问题的原因是:HTTP1.x的请求响应模型是客户端发完请求后要么主动关闭写方向,要么服务端返回完响应后会主动要求关闭连接,两端的EOF都会正常触发,所以大概率不会卡,但不符合通用TCP场景要求。

通用TCP代理的正确实现

核心是要正确处理TCP半关闭逻辑:当任意一个方向的转发结束后,主动关闭对应连接的写方向,通知对端该方向已经没有更多数据发送,不要用Shutdown::Both直接关闭整个连接,否则可能丢失另一个方向还在传输的数据。
修正后的代码如下:

use std::net::{TcpStream, Shutdown};
use std::io;
use std::thread;

type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>;

pub fn route(source: TcpStream, worker: Worker) -> Result<()> {
    let mut source = source;
    let mut destination = TcpStream::connect(worker.address)?;

    let mut source_read = source.try_clone()?;
    let mut dest_write = destination.try_clone()?;
    // 客户端->后端转发线程
    let src2dst = thread::spawn(move || -> io::Result<u64> {
        let res = io::copy(&mut source_read, &mut dest_write);
        // 客户端已经没有数据发送,关闭到后端的写方向,通知后端EOF
        let _ = dest_write.shutdown(Shutdown::Write);
        res
    });

    let mut dest_read = destination;
    let mut source_write = source;
    // 后端->客户端转发线程
    let dst2src = thread::spawn(move || -> io::Result<u64> {
        let res = io::copy(&mut dest_read, &mut source_write);
        // 后端已经没有数据发送,关闭到客户端的写方向,通知客户端EOF
        let _ = source_write.shutdown(Shutdown::Write);
        res
    });

    // 生产环境可按需处理错误、打印日志,不要直接用unwrap
    let _ = src2dst.join()??;
    let _ = dst2src.join()??;

    Ok(())
}

注意事项

  • 上述实现可以覆盖所有通用TCP场景,包括需要半关闭的协议(比如FTP、SSH、WebSocket等)
  • 如果代理的并发量较大,多线程模型的开销会比较高,可以考虑改用tokio等异步运行时实现,性能提升会非常明显

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.28 04:24:02