我知道有bind: cannot assign requested address个和Cannot assign requested address - possible causes?个,但我一直无法从这些问题中得出解决方案.

我正在try 使用libc直接创建一个TCP流(特别是这里的std::net::TcpListener).我在运行代码时遇到错误Cannot assign requested addresserror code: 99.

确切的输出是:

error message: error code: : Cannot assign requested address
thread 'main' panicked at 'error code: 99', src/main.rs:43:13
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

我的代码(playground):

use libc; // 0.2.136

use std::mem::{size_of, transmute};

fn main() {
    // Create socket
    let socket_fd = unsafe {
        let socket = libc::socket(libc::AF_INET6, libc::SOCK_STREAM | libc::SOCK_NONBLOCK, 0);
        assert_ne!(socket, -1);
        let optval = 1i32;
        let res = libc::setsockopt(
            socket,
            libc::SOL_SOCKET,
            libc::SO_REUSEPORT,
            (&optval as *const i32).cast(),
            4,
        );
        assert_eq!(res, 0);
        socket
    };

    // Bind socket
    
    // decimal 127.0.0.1 -> hexadecimal 007f.0000.0000.0001
    let sin6_addr = unsafe { transmute::<_, libc::in6_addr>(*b"007f000000000001") };
    let socket_address = libc::sockaddr_in6 {
        sin6_family: libc::AF_INET6 as u16,
        sin6_port: 8080,
        sin6_flowinfo: u32::default(),
        sin6_addr,
        sin6_scope_id: u32::default(),
    };
    let socket_address_length = size_of::<libc::sockaddr_in6>() as u32;
    unsafe {
        let res = libc::bind(
            socket_fd,
            (&socket_address as *const libc::sockaddr_in6).cast(),
            socket_address_length,
        );
        if res == -1 {
            let err = errno();
            print_error("error message: ");
            panic!("error code: {err}");
        }
    }
    
    assert_eq!(unsafe { libc::close(socket_fd) },0);
}

fn print_error(s: &str) {
    unsafe {
        libc::perror(s.as_ptr().cast());
    }
}
fn errno() -> i32 {
    unsafe { *libc::__errno_location() }
}

我在这里设置了选项SO_REUSEPORT,因为我需要创建多个进程可以监听的流.

https://lwn.net/Articles/542629/:

SO_REUSEPORT的基本概念非常简单.多个服务器(进程或线程)可以绑定到同一端口

端口似乎未在使用中:

jonathan@jonathan-pc:~$ sudo lsof -i -P -n
COMMAND    PID            USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
systemd-r  793 systemd-resolve   13u  IPv4  19452      0t0  UDP 127.0.0.53:53 
systemd-r  793 systemd-resolve   14u  IPv4  19453      0t0  TCP 127.0.0.53:53 (LISTEN)
avahi-dae  855           avahi   12u  IPv4  31377      0t0  UDP *:5353 
avahi-dae  855           avahi   13u  IPv6  31378      0t0  UDP *:5353 
avahi-dae  855           avahi   14u  IPv4  31379      0t0  UDP *:49594 
avahi-dae  855           avahi   15u  IPv6  31380      0t0  UDP *:58397 
NetworkMa  859            root   36u  IPv4  36212      0t0  UDP 192.168.0.22:68->192.168.0.1:67 
NetworkMa  859            root   37u  IPv4 110801      0t0  UDP 192.168.0.17:68->192.168.0.1:67 
tor       1038      debian-tor    6u  IPv4  31407      0t0  TCP 127.0.0.1:9050 (LISTEN)
rust-anal 4117        jonathan   46u  IPv4  33176      0t0  UDP 127.0.0.1:35972->127.0.0.53:53 
rust-anal 4189        jonathan   46u  IPv4  33176      0t0  UDP 127.0.0.1:35972->127.0.0.53:53 
firefox   4297        jonathan    3u  IPv4 112786      0t0  TCP 192.168.0.22:46702->151.101.1.69:443 (ESTABLISHED)
firefox   4297        jonathan   29u  IPv4 100032      0t0  TCP 192.168.0.22:55828->34.120.208.123:443 (ESTABLISHED)
firefox   4297        jonathan   56u  IPv4 115182      0t0  TCP 192.168.0.22:50798->52.51.190.37:443 (ESTABLISHED)
firefox   4297        jonathan   75u  IPv4  95741      0t0  TCP 192.168.0.22:48466->142.250.200.10:443 (ESTABLISHED)
firefox   4297        jonathan   81u  IPv4  67879      0t0  TCP 192.168.0.22:55638->34.214.17.205:443 (ESTABLISHED)
firefox   4297        jonathan  116u  IPv4 111739      0t0  TCP 192.168.0.22:37986->172.217.169.68:443 (ESTABLISHED)
firefox   4297        jonathan  121u  IPv4  95751      0t0  TCP 192.168.0.22:46054->198.252.206.25:443 (ESTABLISHED)
firefox   4297        jonathan  129u  IPv4 102073      0t0  TCP 192.168.0.22:51478->34.120.237.76:443 (ESTABLISHED)
firefox   4297        jonathan  136u  IPv4  96742      0t0  TCP 192.168.0.22:36606->34.120.115.102:443 (ESTABLISHED)
firefox   4297        jonathan  139u  IPv4  97943      0t0  TCP 192.168.0.22:36626->172.217.169.68:443 (ESTABLISHED)
firefox   4297        jonathan  144u  IPv4  99520      0t0  TCP 192.168.0.22:49264->198.252.206.25:443 (ESTABLISHED)
firefox   4297        jonathan  157u  IPv4 104859      0t0  TCP 192.168.0.22:58042->93.184.220.29:80 (ESTABLISHED)
jonathan@jonathan-pc:~$ 

推荐答案

你正在把b"007f000000000001"变成libc::in6_addr.但我不认为这是正确的做法.

查看手册页,该 struct 是:

struct in6_addr {
   uint8_t   s6_addr[16];
};

也就是说,只有16个字节,这对于IPv6来说是合适的.但是您的字节实际上是该字符串的ASCII值:30303766...,虽然从技术上讲它是一个IPv6地址,但它不是您的本地地址,所以您无法绑定到它.

此外,在IPv6中,正确的本地主机地址是::1,而不是127.0.0.1.那就是15个0后面跟着一个1.

如果要使用transmute绑定到IPv6本地主机,则:

let sin6_addr = unsafe { transmute::<_, libc::in6_addr>([0_u8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1]) };

或者,如果您坚持使用文字字符串(为什么?):

let sin6_addr = unsafe { transmute::<_, libc::in6_addr>(*b"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x01") };

Linux相关问答推荐

我想强调某些条件是否与Linux中的全部输出匹配

OpenCV编译错误&未定义的引用&Quot;

AWK:按第一列匹配两个文件不起作用

如何使用 gdb 调试堆栈分段错误?

sed:用空格替换

中的换行符

bind(): "无法分配请求的地址"

如何在vim中使用正则表达式来切换文件中所有字符的大小写

nohup 是否可以跨管道工作?

是否可以使用 shell 脚本递归地创建文件夹?

无法覆盖符号链接 RedHat Linux

如何像 Nautilus 那样从命令行挂载?

禁用内存地址的随机化

Linux 上 pid_t、uid_t、gid_t 的大小

将 $_GET 参数传递给 cron 作业(job)

将 AWK 结果分配给变量

如何从 Linux 中的用户空间访问物理地址?

linux根据文件名模式搜索文件

初学者如何在 Linux 中开始使用 Mono?

grep 递归查找 Linux 上的特定文件类型

从文件中随机 Select 行而不用 Unix 啜饮它