Best practice to quit a `TcpListener` in a clean way

From the docs:

use std::net::{TcpListener, TcpStream};

fn handle_client(stream: TcpStream) {
    // ...
}

fn main() -> std::io::Result<()> {
    let listener = TcpListener::bind("127.0.0.1:80")?;

    // accept connections and process them serially
    for stream in listener.incoming() {
        handle_client(stream?);
    }
    Ok(())
}

What is the cleanest way to have a server that waits for connections but is also stoppable by SIGINT / CTRL C? There is a dedicated crate for that but the problem is that the loop will block forever and does not give a way to stop it by setting a boolean. One alternative to loop over accept but accept also blocks until a connection comes in.

The only way to stop a TcpListener from listening is to open a connection to it.

2 Likes

Maybe you could use set_nonblocking()?

Maybe use tokio::net::TcpListener, and wait on your exit channel concurrently with next/accept.
Maybe even straight up tokio::signal

1 Like

Also was my first idea but I run into the thing explained there in the docs:

          // wait until network socket is ready, typically implemented
            // via platform-specific APIs such as epoll or IOCP
            wait_for_fd();
            continue;

which is out of my skillset

Thx I didn't know of Tokio (The book only mentions the standard library)
Will have a look.

another option would be to create 2 channels one for connection handling and one for sending commands and then selecting on those channels. Standard library doesnt provide a select function for channels but crossbeam channels provide a macro for this

1 Like