【问题标题】:Read fragmented TCP packets with TcpStream in Tokio在 Tokio 中使用 TcpStream 读取分段的 TCP 数据包
【发布时间】:2020-08-01 05:49:02
【问题描述】:

我在读取 TCP 数据包时遇到了一些问题。

我正在尝试读取 JSON 响应,大小为 5000 字节,但查看 Wireshark 中的数据包,它们被分成三个不同的数据包,第一个和第二个是 1448 字节,第三个是大小为 2530 字节。

当我尝试使用Tokio-rs 读取它们时,我只收到第一个,所以我没有收到完整的 JSON 数据。

为了阅读,我使用以下代码:

pub async fn read(stream: &mut TcpStream) -> Result<Bytes, std::io::Error>{
    let mut buf = BytesMut::with_capacity(8128);
    let mut resp = [0u8; 8128];
    let buf_len = stream.read(&mut resp).await?;
    buf.extend_from_slice(&resp);
    buf.truncate(buf_len);
    println!("{}", buf.len());
    Ok(buf.freeze())
}

buf.len() 返回的1448 正是第一个和第二个数据包的大小,但buf 包含来自第一个数据包的数据。

现在我想知道我是否遗漏了什么,TcpStream 在收到第一个数据包时关闭,还是我在某处遗漏了缓冲区大小。

【问题讨论】:

  • 您看到的结果就是 Read Trait 的工作原理,不是吗?您可能需要多次调用 read 或使用 read_to_end() 之类的方法。
  • @JussiKukkonen read_to_end() 对我不起作用,因为它会在套接字关闭时停止读取,但我的不会。

标签: tcp rust rust-tokio


【解决方案1】:

读取方法,如Read::readAsyncReadExt::read,通常不保证每个人将消耗多少数据。如果TcpStream 有三个可用的数据包,它可能只消耗第一个数据包,或者前两个数据包,或者第一个数据包和第二个数据包的一半。无论它做什么都是实现细节。您可以做出的唯一假设是,如果它返回 0(即未读取任何字节),则它已到达其“流结束”(例如,因为连接已关闭)。

因此,一般应该循环阅读:

let mut buf = BytesMut::with_capacity(8128);
let mut resp = [0u8; 8128];

loop {
    let buf_len = stream.read(&mut resp).await?;
    buf.extend_from_slice(&resp[0..buf_len]);

    if buf_len == 0 {
      // end of stream
      panic!("Unexpected EOF");
    } else if buf.len() >= 5000 {
      //      ^---------------^
      //               \_________ some condition to check if buffer is "ready"

      // buffer has been filled with enough bytes
      break;
    } else {
      // buffer does not have enough bytes, keep reading...
      continue;
    }
}

println!("{}", buf.len());
Ok(buf.freeze())

或者,如果你想填满整个缓冲区,你可以改用read_exact方法,它会循环读取直到缓冲区满,或者read_to_end,它会一直读到结束已到达流。

【讨论】:

    猜你喜欢
    • 2020-08-06
    • 1970-01-01
    • 2011-07-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多