How to finish UDP server? (drop UdpSocket doesn't work)

I am building Tauri desktop application. In that, main process (in rust) handles UDP server on a single NIC.
This application must be able to change NIC on user's operation.
The server works well, but when I change NIC, UdpSocket on former NIC goes on and show error when I change back again to former NIC (I want reconstruct server) with "address in use".
I know it is necessary to finish the UdpSocket (which is recv_from-ing in a spawned thread) but I don't know how to do it.

  • server struct (simplified)
pub struct MyUdpServer {
  pub ip: String,
  pub port: u16,
  maybe_socket: Option<UdpSocket>,
}
  • server construction (simplified and imcomplete)
impl MyUdpServer {
  pub fn new(config: ServerConfig) -> Result<Self, String>
  {
    let target: String = format!("{0}:{1}", config.ip, config.port);
    let sock: UdpSocket = UdpSocket::bind(addr: target).unwrap();
    let sock_clone = sock.try_clone().unwrap(); // for waiting messages...
    let server: MyUdpServer = MyUdpServer {
      ip: config.ip,
      port: config.port,
      maybe_socket: Some(sock),
    };
    // start receiving
    let mut buffer = [u8; 1500];
    let _handle = thread::spawn(move || loop {
      match sock_clone.recv_from(buf: &mut buffer) {
        Err(e) => { break; } // actually, reporting error
        Ok((size, src)) => { ... } // actually, processing message
      }
    });
    // return this struct (success)
    Ok(server)
  } // end of fn new
}
  • Drop
impl Drop for MyUdpServer {
    fn drop(&mut self) {
        let s = self.maybe_socket.take();
        if let Some(soc) = s {
            drop(soc);
        }
    }
}

The drop has no effect because it drops sock, not sock_clone. You need to also drop sock_clone, but that is difficult since the thread is making use of it.

Possible solutions, ordered from smallest to most powerful:

  • Add a flag shared with the receiving thread (an Arc<AtomicBoolean>, say) which tells its loop when to exit. Then, send yourself a UDP packet to wake up the recv_from and cause the flag to be checked.

  • A general solution to cancellation/shutdown problems like this is to switch to async, e.g. using tokio::net::UdpSocket. Unlike threads, async tasks can be cancelled at any time, which in this case will naturally cause the socket to drop too.

  • You can run the receiver in a subprocess and kill the subprocess. This has the advantage of being guaranteed to succeed, even if the receiving thread is stuck doing a lot of work on just one packet. Servers sometimes have separate “supervisor” processes for this and other reasons.

I would personally go for async.

1 Like

Thank you for reply!
Your solutions are very clear. I consider first or second options for now.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.