问题
Editor's note: This code example is from a version of Rust prior to 1.0 and is not valid Rust 1.0 code. The concepts discussed in the question are still valid.
I'm experimenting with torrent scraping using Rust. I can see the incoming package in Wireshark, but my recv_from
calls always return Error("End of file")
. Here's my program:
use std::io::net::ip::{Ipv4Addr, SocketAddr};
use std::io::net::udp::UdpSocket;
use std::rand;
use std::io::MemWriter;
fn main() {
let addr = SocketAddr { ip: Ipv4Addr(0, 0, 0, 0), port: 35000 };
let mut socket = match UdpSocket::bind(addr) {
Ok(s) => s,
Err(e) => panic!("couldn't bind socket: {}", e),
};
let mut buf: Vec<u8> = Vec::with_capacity(1000);
let transaction_id: u32 = rand::random();
let mut req_data = MemWriter::with_capacity(16);
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.get_ref(), ("31.172.63.252", 80)));
match socket.recv_from(buf.as_mut_slice()) {
Ok((amt, src)) => {
println!("Got {} bytes from {}.", amt, src);
},
Err(err) => println!("Can't recv_from: {}", err)
}
}
The output is always:
➜ udp-bug git:(master) ✗ cargo run
Compiling udp-bug v0.0.1 (file:///home/omer/rust/udp-bug)
Running `target/udp-bug`
Ok(())
Can't recv_from: end of file
However, I can see the expected response coming in Wireshark:
20235 3512.148636000 31.172.63.252 192.168.1.4 QUIC 60 CID: 0, Seq: 0
This package has a 16-byte payload, exactly what I expect. What's going wrong?
回答1:
Editor's note: This code example is from a version of Rust prior to 1.0 and is not valid Rust 1.0 code. The concepts discussed in the answer are still valid.
I think your problem is that you're using Vec::with_capacity()
as a mutable slice. Vec::with_capacity()
only creates a vector with the specified capacity (naturally), but its length is zero. Consequently, the length of the slice taken from the vector will also be zero:
let v = Vec::with_capacity(128);
println!("{}", v.as_mut_slice().len()); // prints 0
Slices can't grow, so recv_from()
has no space to write to and it fails with the error.
You have essentially two options here. First one is to use unsafe set_len()
method:
let mut buf: Vec<u8> = Vec::with_capacity(1000);
unsafe { buf.set_len(1000); }
This way the buffer will have the correct length but its contents will likely be just garbage. This is not very important for this use case, however, as long as you only access the correct amount of bytes (using the information returned by recv_from()
).
There is a better way, however. You can use stack-allocated fixed-size array:
let mut buf = [0u8, ..1000];
// ...
match socket.recv_from(buf.as_mut_slice()) {
// ...
}
Same thing goes for your req_data
: you can use a statically sized array and a 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)));
This will only work with fixed-size buffers though. If you don't know the size of the buffer, you will still need a Vec
.
来源:https://stackoverflow.com/questions/26778657/udpsocket-recv-from-fails-with-end-of-file-but-i-can-see-the-incoming-package