2019-03-26 Notes on Socket Reuse in Std

Problem

When wanting to perform local multicast in Rust (for example for mDNS), you need to setup the socket as “reusable” before connecting to it. Currently this is hard to do with std, and instead the net2 crate is commonly used. This is problematic, because for async IO this means sockets must be initialized as blocking.

Ideally we could enable ways to set options on sockets in Rust before connecting.

Note: this isn’t likely to be a big problem for UDP connections specifically, but for e.g. TcpListener::accept() this can actually become a bottleneck. What we’re going for here is a way to uniformly set the options on all socket types.

Prior discussions


  • @kali, @danslapman it is correct that there is currently no method for doing this, but this was explicitly discussed in the RFC. Adding this sort of behavior will likely not be done through a new bind_reusable constructor but rather a more general mechanism of starting with a Socket, settings some options, and then later binding or connecting it into a UdpSocket or TcpStream for example.
  • These sorts of additions are definitely planned for, but are all backwards compatible extensions to the API. An RFC will be required, however, to add this new API as it was not explicitly designed in rust-lang/rfcs#807


Example code to enable reuse

Must be called before a socket is bound, which isn’t always possible.

fn enable_port_reuse(socket: &UdpSocket) -> std::io::Result<()> {
    use std::os::unix::prelude::*;
    use std::mem;
    use libc;

    unsafe {
        let optval: libc::c_int = 1;
        let ret = libc::setsockopt(
            socket.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(std::io::Error::last_os_error());
        }
    }

    Ok(())
}

Resources