Search code examples
multithreadingtcprusttcpservercancellation

How do I cancel a TCPListener that is in a listening loop from outside the loop?


I have a TCP server listening on requests in an infinite loop:

use std::io::prelude::*;
use std::net::TcpStream;
use std::net::TcpListener;

fn main() {
    let listener = TcpListener::bind("0.0.0.0:7878").unwrap();

    for stream in listener.incoming() {
        let mut stream = stream.unwrap();

        let response = "HTTP/1.1 200 OK\r\n\r\nsdfuhgsjdfghsdfjk";
        stream.write(response.as_bytes()).unwrap();
        stream.flush().unwrap();
    }
}

How can I break the loop after a period of time (timeout) ?

After the timeout elapsed, I need the listening loop to stop:

  • right that moment if there is no incoming stream (i.e if there is no streams incoming and there might not be any more in the future, I need the server to stop waiting in vain)
  • after processing one last stream if there is already one incoming at that moment

Solution

  • There are three possible solutions to cancel the blocking listener.incoming() call detailed in the Stack Overflow question "Graceful exit TcpListener.incoming()":

    1. Make the listener non-blocking (listener.set_nonblocking(true)), and checking if the timeout has expired when the iterator returns an io::ErrorKind::WouldBlock error.
    2. Using the nix::poll crate to use an event loop to process events. Adding an extra file descriptor that is written when the timeout occurred would allow this to about the loop.
    3. On a Unix system, you could use let fd = listener.as_raw_fd(); before the loop, and then call libc::shutdown(fd, libc::SHUT_RD); to cause the incoming iterator to return an error.

    I also found the cancellable_io crate that replaces TcpListener and implements cancelation.

    IMHO it is unfortunate that the Rust std::net doesn't include a method to cancel the listener.

    Perhaps this question should be marked as a duplicate of the mentioned Stack Overflow question, but I don't have the reputation to do so.