Rust网络编程超时控制库timeout-readwrite的使用,实现读写操作的精确超时管理

Rust网络编程超时控制库timeout-readwrite的使用,实现读写操作的精确超时管理

timeout-readwrite是一个Rust库,提供了带超时功能的Reader和Writer结构体。

为什么需要这个库?

当我们使用异步I/O时,有时需要与后台子进程交互。例如,子进程等待某个事件发生并在发生时输出消息到标准输出。我们希望程序能阻塞等待子进程的输出,但如果子进程挂起,程序将永远阻塞。使用TimeoutReader可以设置读取超时,在超时后返回错误。

示例代码

基础示例(无超时控制)

use std::io::{BufRead, BufReader};
use std::io::Result;
use std::process;

fn each_line<R: BufRead>(rdr: R) -> Result<()> {
    let lines = rdr.lines();

    for rslt_line in lines {
        let line = rslt_line?;
        println!("{}", line);
    }
    Ok(())
}

fn do_command(cmd: &str) -> Result<()> {
    let mut cmd = process::Command::new(cmd);
    let child = cmd.stdout(process::Stdio::piped())
        .stderr(process::Stdio::null())
        .spawn().expect("spawning did not succeed");

    let stdout = child.stdout?;
    each_line(BufReader::new(stdout))
}

使用TimeoutReader添加超时控制

use std::time::Duration;

fn do_command(cmd: &str) -> Result<()> {
    let mut cmd = process::Command::new(cmd);
    let child = cmd.stdout(process::Stdio::piped())
        .stderr(process::Stdio::null())
        .spawn().expect("spawning did not succeed");

    let stdout = child.stdout?;
    each_line(BufReader::new(TimeoutReader::new(stdout, Duration::new(5, 0))))
}

完整示例

use std::io::{BufRead, BufReader, Result};
use std::process;
use std::time::Duration;
use timeout_readwrite::TimeoutReader;

// 处理每一行输出的函数
fn each_line<R: BufRead>(rdr: R) -> Result<()> {
    let lines = rdr.lines();

    for rslt_line in lines {
        match rslt_line {
            Ok(line) => println!("{}", line),
            Err(e) => {
                eprintln!("读取错误: {}", e);
                break;
            }
        }
    }
    Ok(())
}

// 执行命令并设置读取超时
fn do_command_with_timeout(cmd: &str) -> Result<()> {
    // 创建子进程
    let mut cmd = process::Command::new(cmd);
    let child = cmd.stdout(process::Stdio::piped())
        .stderr(process::Stdio::null())
        .spawn()
        .expect("启动子进程失败");

    // 获取子进程的标准输出
    let stdout = child.stdout?;
    
    // 创建带5秒超时的TimeoutReader
    let timeout_reader = TimeoutReader::new(stdout, Duration::new(5, 0));
    
    // 使用带缓冲的读取器处理输出
    each_line(BufReader::new(timeout_reader))
}

fn main() {
    if let Err(e) = do_command_with_timeout("your_command") {
        eprintln!("命令执行错误: {}", e);
    }
}

使用方法

  1. 在Cargo.toml中添加依赖:
[dependencies]
timeout-readwrite = "0.4.0"
  1. 在代码中使用:
extern crate timeout_readwrite;
use timeout_readwrite::TimeoutReader;
use std::time::Duration;

工作原理

TimeoutReader包装了现有的Reader,在每次读取操作时检查是否超过指定的超时时间。如果超过超时时间,读取操作将返回ErrorKind::TimeOut错误。

现在,如果程序等待子进程输出的时间超过5秒而没有收到数据,读取操作将失败并返回超时错误,而不是无限期阻塞。


1 回复

Rust网络编程超时控制库timeout-readwrite使用指南

timeout-readwrite是一个专门为Rust网络编程设计的库,它提供了精确的读写操作超时控制功能,帮助开发者避免网络I/O操作无限期阻塞的问题。

主要特性

  • ReadWrite trait提供超时包装
  • 精确到毫秒级别的超时控制
  • 支持标准库和Tokio异步运行时
  • 简单易用的API设计

安装方法

Cargo.toml中添加依赖:

[dependencies]
timeout-readwrite = "0.3"

基本使用方法

同步I/O超时控制

use std::net::TcpStream;
use std::time::Duration;
use timeout-readwrite::TimeoutReader;

fn main() -> std::io::Result<()> {
    let stream = TcpStream::connect("example.com:80")?;
    let mut reader = TimeoutReader::new(stream, Duration::from_secs(5));
    
    let mut buf = [0; 1024];
    match reader.read(&mut buf) {
        Ok(n) => println!("Read {} bytes", n),
        Err(e) if e.kind() == std::io::ErrorKind::TimedOut => {
            println!("Read operation timed out");
        }
        Err(e) => return Err(e),
    }
    
    Ok(())
}

异步I/O超时控制(Tokio)

use tokio::net::TcpStream;
use std::time::Duration;
use timeout-readwrite::TimeoutReaderExt;

#[tokio::main]
async fn main() -> std::io::Result<()> {
    let stream = TcpStream::connect("example.com:80").await?;
    let mut reader = stream.timeout_reader(Duration::from_secs(5));
    
    let mut buf = [0; 1024];
    match reader.read(&mut buf).await {
        Ok(n) => println!("Read {} bytes", n),
        Err(e) if e.kind() == std::io::ErrorKind::TimedOut => {
            println!("Read operation timed out");
        }
        Err(e) => return Err(e),
    }
    
    Ok(())
}

高级用法

读写分别设置不同超时

use std::net::TcpStream;
use std::time::Duration;
use timeout-readwrite::{TimeoutReader, TimeoutWriter};

fn main() -> std::io::Result<()> {
    let stream = TcpStream::connect("example.com:80")?;
    
    // 读超时5秒,写超时10秒
    let mut reader = TimeoutReader::new(stream.try_clone()?, Duration::from_secs(5));
    let mut writer = TimeoutWriter::new(stream, Duration::from_secs(10));
    
    // 读取数据
    let mut buf = [0; 1024];
    reader.read(&mut buf)?;
    
    // 写入数据
    writer.write_all(b"GET / HTTP/1.1\r\nHost: example.com\r\n\r\n")?;
    
    Ok(())
}

动态调整超时时间

use std::net::TcpStream;
use std::time::Duration;
use timeout-readwrite::TimeoutReader;

fn main() -> std::io::Result<()> {
    let stream = TcpStream::connect("example.com:80")?;
    let mut reader = TimeoutReader::new(stream, Duration::from_secs(5));
    
    // 初始读取使用5秒超时
    let mut buf = [0; 1024];
    reader.read(&mut buf)?;
    
    // 后续读取调整为2秒超时
    reader.set_timeout(Duration::from_secs(2));
    reader.read(&mut buf)?;
    
    Ok(())
}

完整示例

下面是一个完整的HTTP客户端示例,演示如何使用timeout-readwrite库进行网络请求:

use std::net::TcpStream;
use std::time::Duration;
use std::io::{Read, Write};
use timeout_readwrite::{TimeoutReader, TimeoutWriter};

fn main() -> std::io::Result<()> {
    // 建立TCP连接
    let stream = TcpStream::connect("example.com:80")?;
    
    // 设置读写超时
    let mut reader = TimeoutReader::new(stream.try_clone()?, Duration::from_secs(5));
    let mut writer = TimeoutWriter::new(stream, Duration::from_secs(3));
    
    // 发送HTTP请求
    writer.write_all(b"GET / HTTP/1.1\r\nHost: example.com\r\nConnection: close\r\n\r\n")?;
    
    // 读取响应
    let mut response = Vec::new();
    let mut buf = [0; 1024];
    
    loop {
        match reader.read(&mut buf) {
            Ok(0) => break, // 连接关闭
            Ok(n) => response.extend_from_slice(&buf[..n]),
            Err(e) if e.kind() == std::io::ErrorKind::TimedOut => {
                println!("读取超时,已读取部分响应");
                break;
            }
            Err(e) => return Err(e),
        }
    }
    
    // 打印响应
    println!("{}", String::from_utf8_lossy(&response));
    
    Ok(())
}

注意事项

  1. 超时错误会被包装为std::io::ErrorKind::TimedOut类型的错误
  2. 对于异步操作,超时是通过Tokio的计时器实现的
  3. 超时精度取决于系统计时器的精度
  4. 频繁设置很短的超时时间可能会影响性能

timeout-readwrite库为Rust网络编程提供了简单可靠的超时控制机制,是构建健壮网络应用的实用工具。

回到顶部