如何从循环外部取消侦听循环中的TCPListener?

时间:2019-02-27 19:40:27

标签: multithreading tcp rust tcpserver cancellation

我有一个TCP服务器在无限循环中侦听请求:

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();
    }
}

如何在一段时间(超时)后中断循环?

超时后,我需要停止监听循环:

  • 在那一刻,如果没有输入流(即,如果没有输入流并且将来可能不再有输入流,我需要服务器停止徒劳地等待)
  • 在处理完最后一个流之后,如果那时已经有一个传入的流

1 个答案:

答案 0 :(得分:0)

我认为您的问题与this一个问题有关。

但是由于您不是尝试将其与tokio一起使用,而是与std库和for循环一起使用,因此您需要通过其他线程进行管理。

您需要拥有

  • 正在接受与listener.incoming()的连接作为非阻塞线程
  • 正在检查连接超时的线程。
  • 主线程和mpsc::channels(),以便在这些线程之间传递数据。

借助通道,您可以通知发生超时的时间,并在接受新连接时重置超时。

由于TcpListener默认为阻止状态,因此您需要使用以下命令将其更改为非阻止状态:

let listener = TcpListener::bind("0.0.0.0:7878").unwrap();
listener
    .set_nonblocking(true)
    .expect("Could not set non blocking");

您可能会像下面这样糟透了:

use std::io::prelude::*;
use std::net::TcpListener;
use std::sync::mpsc::channel;
use std::thread;
use std::time::Duration;

fn main() {
    let listener = TcpListener::bind("0.0.0.0:7878").unwrap();
    listener
        .set_nonblocking(true)
        .expect("set_nonblocking call failed");

    let (main_sender, main_receiver) = channel();
    let main_sender_for_timeout_thread = main_sender.clone();
    let main_sender_for_server_thread = main_sender.clone();

    let (main_bus_sender, main_bus_receiver) = channel();
    let main_bus_sender_for_timeout_thread = main_bus_sender.clone();
    let main_bus_sender_for_server_thread = main_bus_sender.clone();

    let timeout_thread = thread::spawn(move || {
        let (timeout_sender, timeout_receiver) = channel();
        let _ = main_bus_sender_for_timeout_thread.send(timeout_sender);

        let mut current_timer = 0;
        loop {
            if current_timer == 10 {
                let _ = main_sender_for_timeout_thread.send("TimeoutOccurred");
                break;
            }

            println!(
                "Waiting for timeout. Current Timer Count: {}",
                current_timer
            );

            if timeout_receiver.try_recv().is_ok() {
                //Reset timer
                current_timer = 0;
            }

            let one_sec = Duration::new(1, 0);
            thread::sleep(one_sec);
            current_timer += 1;
        }
    });

    let timeout_sender = main_bus_receiver.recv().unwrap();

    let server_thread = thread::spawn(move || {
        let (server_sender, server_receiver) = channel();

        let _ = main_bus_sender_for_server_thread.send(server_sender);

        for stream in listener.incoming() {
            if let Ok(mut stream) = stream {
                println!("Connection Accepted");
                let response = "HTTP/1.1 200 OK\r\n\r\nsdfuhgsjdfghsdfjk";
                stream.write(response.as_bytes()).unwrap();
                stream.flush().unwrap();
                let _ = main_sender_for_server_thread.send("ResetTimeout");
            }

            if let Ok(msg) = server_receiver.try_recv() {
                if msg == "CloseServer" {
                    println!("Closing the server thread");
                    break;
                }
            }
        }
    });

    let server_sender = main_bus_receiver.recv().unwrap();

    loop {
        let message = main_receiver.recv().unwrap();
        if message == "ResetTimeout" {
            println!("Connection established, resetting timeout");
            let _ = timeout_sender.send("");
        }

        if message == "TimeoutOccurred" {
            println!("TimeoutOccurred");
            let _ = server_sender.send("CloseServer"); // This message closes the server
            break;
        }
    }

    let _ = timeout_thread.join();
}

请注意,最好使用incoming as future streamfutures::mpsc()在这些期货之间进行选择并适当地关闭。在我提供的示例中,您正在删除服务器线程而不是取消绑定它,这可以视为强制关闭。


有关使用期货板条箱的更好方法,您可以检查following