如何“连接”一个 UDP 套接字并让 OS 选择端口和地址

How do I `connect` an UDP socket and let the OS choose the port and address

我想向服务器发送一个 UDP 查询并接收它的响应。在 C 中是:call socket then connect then write then read。操作系统负责选择合适的本地 IP 地址和端口来使用。

我正尝试在 Rust 中做同样的事情。但是如果我自己不指定地址和端口,我找不到获得 UdpSocket 的方法。我能做的最接近的是:

fn main() {
    use std::net::{Ipv4Addr, UdpSocket};
    let socket = UdpSocket::bind((Ipv4Addr::UNSPECIFIED, 12345)).unwrap();
    socket.connect("1.1.1.1:53").unwrap();
    socket.send(b"\x12\x34\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\
                \x07example\x03com\x00\
                \x00\x01\x00\x01").unwrap();
    let mut buffer = [0; 512];
    let len = socket.recv(&mut buffer).unwrap();
    for b in &buffer[0..len] {
        print!("{:02x} ", b);
    }
    println!("");
}

这可行,但与 C 版本相比有两个缺点。

  1. 如果我指定一个已经在使用的本地端口,它可能会失败,

  2. 套接字监听所有个可用地址,而C版本只监听“合适的”地址。这对于 UDP 很重要,可以避免响应欺骗。

    比如我查询127.0.0.1,那么C版会自动绑定到127.0.0.1地址。互联网将无法欺骗我的查询的答案。另外,如果我有两个网络接口,一个连接到互联网,一个连接到我的本地网络,IP 为 192.168.0.1,我可以查询本地网络上的解析器,如 172.16.17.18。 C 版本将绑定到地址 192.168.0.1,我将确保答案不是来自互联网。

我怎样才能最好地做到与 C 版本相同?

编辑:解释关于寻找“合适”地址的第二点。

这并不明显,但是如果您将套接字绑定到 (UNSPECIFIED, 0),那么当您连接到远程地址时,本地地址将设置为适当的接口地址。 (端口0表示自动分配一个端口,就像C中的未绑定套接字一样。)您可以在连接成功后在套接字上调用local_addr()来确认这一点。

对于 POSIX 的复杂性,我深表歉意。 :-)