\n\n\n编者注:此代码示例来自 Rust 1.0 之前的版本,不是有效的 Rust 1.0 代码。问题中讨论的概念仍然有效。
\n
我正在尝试使用 Rust 进行 torrent 抓取。我可以在 Wireshark 中看到传入的包,但我的recv_from呼叫总是返回Error("End of file"). 这是我的程序:
use std::io::net::ip::{Ipv4Addr, SocketAddr};\nuse std::io::net::udp::UdpSocket;\nuse std::rand;\n\nuse std::io::MemWriter;\n\nfn main() {\n let addr = SocketAddr { ip: Ipv4Addr(0, 0, 0, 0), port: 35000 };\n let mut socket = match UdpSocket::bind(addr) {\n Ok(s) => s,\n Err(e) => panic!("couldn\'t bind socket: {}", e),\n };\n\n let mut buf: Vec<u8> = Vec::with_capacity(1000);\n\n let transaction_id: u32 = rand::random();\n let mut req_data = MemWriter::with_capacity(16);\n req_data.write_be_u64(0x41727101980).unwrap(); // connection_id, identifies the protocol.\n req_data.write_be_u32(0).unwrap(); // action: connect\n req_data.write_be_u32(transaction_id).unwrap();\n\n println!("{}", socket.send_to(req_data.get_ref(), ("31.172.63.252", 80)));\n\n match socket.recv_from(buf.as_mut_slice()) {\n Ok((amt, src)) => {\n println!("Got {} bytes from {}.", amt, src);\n },\n Err(err) => println!("Can\'t recv_from: {}", err)\n }\n}\nRun Code Online (Sandbox Code Playgroud)\n\n输出始终是:
\n\nuse std::io::net::ip::{Ipv4Addr, SocketAddr};\nuse std::io::net::udp::UdpSocket;\nuse std::rand;\n\nuse std::io::MemWriter;\n\nfn main() {\n let addr = SocketAddr { ip: Ipv4Addr(0, 0, 0, 0), port: 35000 };\n let mut socket = match UdpSocket::bind(addr) {\n Ok(s) => s,\n Err(e) => panic!("couldn\'t bind socket: {}", e),\n };\n\n let mut buf: Vec<u8> = Vec::with_capacity(1000);\n\n let transaction_id: u32 = rand::random();\n let mut req_data = MemWriter::with_capacity(16);\n req_data.write_be_u64(0x41727101980).unwrap(); // connection_id, identifies the protocol.\n req_data.write_be_u32(0).unwrap(); // action: connect\n req_data.write_be_u32(transaction_id).unwrap();\n\n println!("{}", socket.send_to(req_data.get_ref(), ("31.172.63.252", 80)));\n\n match socket.recv_from(buf.as_mut_slice()) {\n Ok((amt, src)) => {\n println!("Got {} bytes from {}.", amt, src);\n },\n Err(err) => println!("Can\'t recv_from: {}", err)\n }\n}\nRun Code Online (Sandbox Code Playgroud)\n\n不过,我可以在 Wireshark 中看到预期的响应:
\n\n\xe2\x9e\x9c udp-bug git:(master) \xe2\x9c\x97 cargo run\n Compiling udp-bug v0.0.1 (file:///home/omer/rust/udp-bug)\n Running `target/udp-bug`\nOk(())\nCan\'t recv_from: end of file\nRun Code Online (Sandbox Code Playgroud)\n\n这个包有一个 16 字节的有效负载,正是我所期望的。出了什么问题?
\n编者注:此代码示例来自 Rust 1.0 之前的版本,不是有效的 Rust 1.0 代码。答案中讨论的概念仍然有效。
我认为你的问题是你正在使用Vec::with_capacity()可变切片。Vec::with_capacity()只创建一个具有指定容量(自然)的向量,但其长度为零。因此,从向量中取出的切片的长度也将为零:
let v = Vec::with_capacity(128);
println!("{}", v.as_mut_slice().len()); // prints 0
Run Code Online (Sandbox Code Playgroud)
切片无法增长,因此recv_from()没有空间可写入,并且会因错误而失败。
这里你基本上有两个选择。第一个是使用不安全的set_len()方法:
let mut buf: Vec<u8> = Vec::with_capacity(1000);
unsafe { buf.set_len(1000); }
Run Code Online (Sandbox Code Playgroud)
这样缓冲区将具有正确的长度,但其内容可能只是垃圾。然而,这对于这个用例来说并不是很重要,只要您只访问正确的字节数(使用 返回的信息recv_from())。
然而,还有更好的方法。您可以使用堆栈分配的固定大小数组:
let mut buf = [0u8, ..1000];
// ...
match socket.recv_from(buf.as_mut_slice()) {
// ...
}
Run Code Online (Sandbox Code Playgroud)
同样的事情也适用于您req_data:您可以使用静态大小的数组和BufWriter:
let transaction_id: u32 = rand::random();
let mut req_data_buf = [0u8, ..16];
let mut req_data = BufWriter::new(req_data_buf);
req_data.write_be_u64(0x41727101980).unwrap(); // connection_id, identifies the protocol.
req_data.write_be_u32(0).unwrap(); // action: connect
req_data.write_be_u32(transaction_id).unwrap();
println!("{}", socket.send_to(req_data_buf, ("31.172.63.252", 80)));
Run Code Online (Sandbox Code Playgroud)
但这仅适用于固定大小的缓冲区。如果您不知道缓冲区的大小,您仍然需要一个Vec.