UdpSocket.recv_from 因“文件结尾”而失败,但我可以在 Wireshark 中看到传入的包

sin*_*nan 2 udp rust recvfrom

\n

编者注:此代码示例来自 Rust 1.0 之前的版本,不是有效的 Rust 1.0 代码。问题中讨论的概念仍然有效。

\n
\n\n

我正在尝试使用 Rust 进行 torrent 抓取。我可以在 Wireshark 中看到传入的包,但我的recv_from呼叫总是返回Error("End of file"). 这是我的程序:

\n\n
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}\n
Run Code Online (Sandbox Code Playgroud)\n\n

输出始终是:

\n\n
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}\n
Run 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\n
Run Code Online (Sandbox Code Playgroud)\n\n

这个包有一个 16 字节的有效负载,正是我所期望的。出了什么问题?

\n

Vla*_*eev 5

编者注:此代码示例来自 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.