根据我对 TCP 的理解,如果我将 X 个字节发送到一个套接字,它几乎可以保证它会到达那里,尽管有效负载可能会 split 并分成两个单独的部分到达(所以如果我等待一个 25 字节的数据包,我可能会得到 20 个字节,然后在下一次读取时再得到 5 个字节)。我从未见过这种情况发生,但我希望在发生这种情况时得到保护。
在过去,我通常设计我的套接字读取来解决这个问题——将传入的字节读入缓冲区,然后不断地检查缓冲区的大小。如果整个数据包有足够的数据,则处理该数据包,从缓冲区前面移除字节,然后继续。
我现在正尝试在 Rust 中编写一个类似的系统,使用 mio
作为 TCP 套接字。我的缓冲区只是一个数组:[u8; MAX_BUFFER_SIZE]
这是我的套接字读取代码:
loop {
// Read until there are no more incoming bytes
match socket.read(&mut buffer) {
Ok(0) => {
// Socket is closed, Client has disconnected!
// (perform disconnection here)
break;
},
Ok(read_bytes) => {
println!("Read {} bytes from client", read_bytes);
},
Err(e) => {
if e.kind() == io::ErrorKind::WouldBlock {
// Socket is not ready anymore, stop reading
break;
}
}
}
}
这不支持读取任何拆分数据,因为 read
函数只是覆盖缓冲区开头的数据,而不是将每个后续调用附加到末尾。在 C# 和 C++ 中,您可以向等效调用提供一个偏移参数以允许此行为,但我不知道如何使用 mio
执行此操作。我找不到任何偏移参数这一事实让我相信我在理解 read
函数时遗漏了一些重要的东西。
我将如何编写解决此问题的套接字读取程序?
最佳答案
The fact that I can't find any offset parameter leads me to believe that I am missing something important
是的,但它与套接字无关。 Rust 有切片,这是一种更通用的解决方案。要获取缓冲区的子集,请从所需的偏移量开始切片。数据将被读入与原始缓冲区的偏移量相同的切片的开头:
socket.read(&mut buffer[offset..])
当你完成读取时,你可以采取一个切片来防止查看缓冲区中无用的尾随数据:
let my_data = &buffer[..total_read_bytes];
// do something with my_data
一个完整的例子:
use std::io::prelude::*;
const MAX_LEN: usize = 64;
fn main() {
let dummy_data = b"this is a very long bit of data";
let mut dummy_data = &dummy_data[..];
let mut buffer = [0; MAX_LEN];
let mut offset = 0;
offset += dummy_data
.by_ref()
.take(4)
.read(&mut buffer[offset..])
.unwrap();
offset += dummy_data
.by_ref()
.take(4)
.read(&mut buffer[offset..])
.unwrap();
let final_data = &buffer[..offset];
let s = std::str::from_utf8(final_data);
println!("{:?}", s);
assert_eq!(s, Ok("this is "));
}
您可能还对 Read::read_exact
感兴趣:
This function reads as many bytes as necessary to completely fill the specified buffer
buf
.
关于sockets - 如何将套接字中的数据读取到现有缓冲区的偏移量中?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48852831/