根据我对TCP的理解,如果我将X字节发送到套接字,它几乎可以保证它会到达那里,尽管有效负载可能会分裂并到达两个独立的部分(所以如果我是等待一个25字节的数据包我可能得到20个字节,然后在下一次读取时再增加5个字节。我从来没有见过这种情况发生过,但是我希望能够得到保障。
在过去,我通常设计我的套接字读取来解释这个问题 - 将传入的字节读入缓冲区,然后不断检查缓冲区的大小。如果整个数据包有足够的数据,请处理该数据包,从缓冲区前面删除字节,然后继续。
我现在正在尝试使用mio
为TCP套接字编写Rust中的类似系统。我的缓冲区只是一个数组:[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
函数时遗漏了一些重要内容。
我如何编写插座读取来解释这个?
答案 0 :(得分:6)
我无法找到任何偏移参数这一事实让我相信我错过了一些重要的事情
是的,但它与套接字没有任何关系。 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
:
此函数根据需要读取尽可能多的字节,以完全填充指定的缓冲区
buf
。