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

为何Rust中TcpStream::connect_timeout()不重试连接?超时未生效

Rust TcpStream::connect_timeout 未按预期阻塞重试连接的问题

我编写了如下Rust客户端代码,预期在200秒内尝试连接服务器,超时后返回错误。但运行该程序时,它立刻返回ConnectionRefused错误,并未按预期尝试连接200秒。我在Linux Ubuntu 22.04系统上运行此程序,为何TcpStream::connect_timeout()没有阻塞执行并重试连接?

客户端代码

use std::{
    io::{self, Result, Write},
    net::{SocketAddr, TcpStream},
    time::Duration,
};

struct Client {
    channel: Option<TcpStream>,
}

impl Client {
    fn new() -> Self {
        Self { channel: None }
    }

    fn connect(&mut self, addr: SocketAddr, duration: Duration) -> Result<()> {
        let connection = TcpStream::connect_timeout(&addr, duration)?;
        self.channel = Some(connection);
        Ok(())
    }

    fn send(&mut self, msg: &[u8]) -> Result<()> {
        self.channel
            .as_mut()
            .ok_or(io::Error::new(io::ErrorKind::NotConnected, "Not connected"))?
            .write_all(msg)?;
        Ok(())
    }
}

fn main() {
    let mut client = Client::new();
    println!("Waiting to connect to server...");
    let result_conn = client.connect(
        SocketAddr::from(([127, 0, 0, 1], 3030)),
        Duration::new(200, 0),
    );

    if result_conn.is_ok() {
        let msg = b"Hi, I am Hernan and this is my first client-server program";
        println!("Sending message: {:?}", msg);
        match client.send(msg) {
            Ok(_) => println!("Message sent"),
            Err(e) => println!("Failed to send message: {:?}", e),
        }
    }
    if let Err(e) = result_conn {
        println!("Fail to connect: {:?}", e);
    }
}

服务端代码

use std::{
    io::{Read, Result},
    net::{SocketAddr, TcpListener, TcpStream},
};

struct Server {
    channel: TcpStream,
}

impl Server {
    const MAX_BUFF_SIZE: usize = 128;

    fn new(addr: SocketAddr) -> Result<Self> {
        println!("Waiting for connections...");
        let listener = TcpListener::bind(addr)?;
        let (stream, socket) = listener.accept()?;
        println!("Connection received from {:?}", socket);
        Ok(Self { channel: stream })
    }

    fn recv(&mut self) -> Result<[u8; Self::MAX_BUFF_SIZE]> {
        let mut buff = [0; Self::MAX_BUFF_SIZE];
        self.channel.read(&mut buff)?;
        Ok(buff)
    }
}

fn main() -> Result<()> {
    let mut server = Server::new(SocketAddr::from(([127, 0, 0, 1], 3030)))?;
    let msg = server.recv()?;
    println!("Message received: {:?}", msg);
    Ok(())
}

问题原因

TcpStream::connect_timeout的作用不是持续重试连接直到超时,它的真实行为是:

  • 发起一次TCP连接尝试,并设置整个连接过程的最大允许耗时(包括DNS解析、TCP握手的时间)
  • 如果目标端口明确处于关闭状态(比如无服务监听该端口),系统内核会直接返回ConnectionRefused错误,不会等待超时时间结束
  • 只有当连接处于"挂起"状态(比如目标主机可达但无响应、网络延迟过高)时,才会等待到设定的超时时间后返回错误

你的场景中,服务端未启动时本地3030端口无监听,系统会立即拒绝连接请求,因此connect_timeout直接返回错误,不会等待200秒。

解决方案

如果需要实现"指定时间内持续重试连接"的逻辑,需自行编写循环重试代码,结合std::thread::sleep控制重试间隔,同时记录总耗时避免超过设定超时时间。

修改后的客户端connect方法示例:

use std::{
    io::{self, Result, Write},
    net::{SocketAddr, TcpStream},
    time::{Duration, Instant},
    thread,
};

// 其余代码保持不变

impl Client {
    // 其余方法保持不变

    fn connect(&mut self, addr: SocketAddr, total_timeout: Duration) -> Result<()> {
        let start_time = Instant::now();
        let retry_interval = Duration::from_secs(1); // 每秒重试一次

        loop {
            match TcpStream::connect(&addr) {
                Ok(stream) => {
                    self.channel = Some(stream);
                    return Ok(());
                }
                Err(e) => {
                    // 仅重试可恢复的连接错误
                    if matches!(e.kind(), io::ErrorKind::ConnectionRefused | io::ErrorKind::TimedOut) {
                        // 检查是否已超过总超时时间
                        if start_time.elapsed() >= total_timeout {
                            return Err(e);
                        }
                        // 等待后重试
                        thread::sleep(retry_interval);
                    } else {
                        // 不可恢复错误直接返回
                        return Err(e);
                    }
                }
            }
        }
    }
}

// main函数保持不变

补充说明

  • 可根据需求调整retry_interval(重试间隔),避免过于频繁的重试占用系统资源
  • 可根据实际场景添加更多需要重试的错误类型
  • 如果需要高效异步重试,可结合Tokio等异步运行时工具实现,避免阻塞主线程

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 17:43:16