如何在Rust中设置套接字选项SO_REUSEPORT?

hur*_*ron 3 linux network-programming tcp rust server

我读过的文档std::net和MIO,我已经找到了一些方法,如set_nodelayset_keepalive,但我还没有找到一种方法来设置像其他套接字选项SO_REUSEPORTSO_REUSEADDR一个给定的插座上.我怎样才能做到这一点?

She*_*ter 7

因为SO_REUSEPORT不是跨平台的,所以您需要深入了解特定于平台的代码.在这种情况下,您可以从套接字获取原始文件描述符,然后使用libc crate中的函数,类型和值来设置所需的选项:

extern crate libc; // 0.2.43

use std::{io, mem, net::TcpListener, os::unix::io::AsRawFd};

fn main() -> Result<(), io::Error> {
    let listener = TcpListener::bind("0.0.0.0:8888")?;

    unsafe {
        let optval: libc::c_int = 1;
        let ret = libc::setsockopt(
            listener.as_raw_fd(),
            libc::SOL_SOCKET,
            libc::SO_REUSEPORT,
            &optval as *const _ as *const libc::c_void,
            mem::size_of_val(&optval) as libc::socklen_t,
        );
        if ret != 0 {
            return Err(io::Error::last_os_error());
        }
    }

    Ok(())
}
Run Code Online (Sandbox Code Playgroud)

我不能保证这是设置此选项的正确位置,或者我没有在不安全的块中搞砸了,但它确实在macOS 10.12上编译和运行.

一个更好的解决方案可能是检查nix crate,它为大多数*nix特定代码提供更好的包装器:

extern crate nix; // 0.11.0

use nix::sys::socket::{self, sockopt::ReusePort};
use std::{error::Error, net::TcpListener, os::unix::io::AsRawFd};

fn main() -> Result<(), Box<Error>> {
    let listener = TcpListener::bind("0.0.0.0:8888")?;
    socket::setsockopt(listener.as_raw_fd(), ReusePort, &true)?;

    Ok(())
}
Run Code Online (Sandbox Code Playgroud)

一个更好的解决方案可能是检查net2 crate,它提供了专门针对网络相关代码的更高级别的方法:

extern crate net2; // 0.2.33

use net2::{unix::UnixTcpBuilderExt, TcpBuilder};

fn main() -> Result<(), std::io::Error> {
    let listener = TcpBuilder::new_v4()?
        .reuse_address(true)?
        .reuse_port(true)?
        .bind("0.0.0.0:8888")?
        .listen(42)?;

    Ok(())
}
Run Code Online (Sandbox Code Playgroud)