在 Tokio 中使用 TcpStream 读取碎片化的 TCP 数据包
Read fragmented TCP packets with TcpStream in Tokio
我在读取 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()
returns 1448
正好是第一个和第二个数据包的大小,但是 buf
包含第一个数据包的数据。
现在我想知道我是否遗漏了什么并且 TcpStream
在收到第一个数据包时关闭,或者我是否在某处缺少缓冲区大小。
读取方法,如 Read::read
or AsyncReadExt::read
,通常不保证每次都会消耗多少数据。如果 TcpStream
有三个数据包可用,它可能只消耗第一个数据包,或者前两个数据包,或者第一个数据包和第二个数据包的一半。无论它做什么都是一个实现细节。您可以做出的唯一假设是,如果它 returns 0
(即没有读取任何字节),它已达到其 "end of stream"(例如因为连接已关闭)。
正因如此,一般应该循环阅读:
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
method instead, which will read in a loop for you until the buffer is full, or read_to_end
,它将读取直到到达流的末尾。
我在读取 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()
returns 1448
正好是第一个和第二个数据包的大小,但是 buf
包含第一个数据包的数据。
现在我想知道我是否遗漏了什么并且 TcpStream
在收到第一个数据包时关闭,或者我是否在某处缺少缓冲区大小。
读取方法,如 Read::read
or AsyncReadExt::read
,通常不保证每次都会消耗多少数据。如果 TcpStream
有三个数据包可用,它可能只消耗第一个数据包,或者前两个数据包,或者第一个数据包和第二个数据包的一半。无论它做什么都是一个实现细节。您可以做出的唯一假设是,如果它 returns 0
(即没有读取任何字节),它已达到其 "end of stream"(例如因为连接已关闭)。
正因如此,一般应该循环阅读:
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
method instead, which will read in a loop for you until the buffer is full, or read_to_end
,它将读取直到到达流的末尾。