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

2023-09-07 13:04:17 作者:山后别相逢

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

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.

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

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)
    }
}

输出总是:

➜  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

但是,我可以在 Wireshark 中看到预期的响应:

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

这个包有一个 16 字节的有效载荷,正是我所期望的.怎么了?

This package has a 16-byte payload, exactly what I expect. What's going wrong?

推荐答案

编者注:此代码示例来自 Rust 1.0 之前的版本,不是有效的 Rust 1.0 代码.答案中讨论的概念仍然有效. 火爆大厂的100道Java面试题及答案 2022年整理,持续更新 网络协议篇

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.

我认为您的问题是您使用 Vec::with_capacity() 作为可变切片.Vec::with_capacity() 仅创建具有指定容量的向量(自然),但其长度为零.因此,从向量中取出的切片长度也将为零:

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 不能增长,所以 recv_from() 没有空间可写,它失败并出现错误.

Slices can't grow, so recv_from() has no space to write to and it fails with the error.

您在这里基本上有两个选择.第一个是使用不安全的 set_len() 方法:

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); }

这样缓冲区将具有正确的长度,但其内容可能只是垃圾.然而,这对于这个用例来说不是很重要,只要您只访问正确数量的字节(使用 recv_from() 返回的信息).

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()) {
    // ...
}

您的 req_data 也是如此:您可以使用静态大小的数组和 BufWriter:

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)));

这仅适用于固定大小的缓冲区.如果你不知道缓冲区的大小,你仍然需要一个 Vec.

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.