首页 > 解决方案 > 如何“连接”一个 UDP 套接字并让操作系统选择端口和地址

问题描述

我想向服务器发送一个 UDP 查询并接收它的响应。在 C 中是:调用socketthen connectthen writethenread。操作系统负责选择合适的本地 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 版本相同的操作?

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

标签: rustudpclient

解决方案


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

我为 POSIX 的复杂性道歉。:-)


推荐阅读