Search code examples
rusttcp

How do I drop the connection in my simple tcp server?


I took a really simple example of a rust server with tcp.

use std::net::{Shutdown,TcpListener, TcpStream};
use std::thread;
use std::io::{Read,Write,Error};

fn handle_client(mut stream: TcpStream)-> Result<(), Error> {
    println!("incoming connection from: {}", stream.peer_addr()?);
    let mut buf = [0;512];
    loop {
        let bytes_read = stream.read(&mut buf)?;
        if bytes_read == 0 {return Ok(())}
        let tmp = format!("{}", String::from_utf8_lossy(&buf).trim());
        eprintln!("getting {}",tmp);
        stream.write(&buf[..bytes_read])?;
    }
}



fn main() {
    
    
    let listener = TcpListener::bind("0.0.0.0:8888").expect("Could not bind");
    for stream in listener.incoming() {
        match stream {
            Err(e)=> {eprintln!("failed: {}", e)}
            Ok(stream) => {
                thread::spawn(move || {
                    handle_client(stream).unwrap_or_else(|error| eprintln!("{:?}", error));
                });
            }
        } 
    }
}

Which basically takes input, spits it back at the client, and prints to it's own terminal.

I would like to be able to end this connection. Ending the connection should probably happen depending on something, but right now I want to just try to shut it down.

I tried looking thorugh the docs, and then tried adding the shutdown method Now, I want to take some stream as input, do something with it, and then shut the channel

So I tried doing this:

fn main() {
    
    
    let listener = TcpListener::bind("0.0.0.0:8888").expect("Could not bind");
    for stream in listener.incoming() {
        match stream {
            Err(e)=> {eprintln!("failed: {}", e)}
            Ok(stream) => {
                
                thread::spawn(move || {
                    handle_client(stream).unwrap_or_else(|error| eprintln!("{:?}", error));
                });
                stream.shutdown(Shutdown::Both).expect("shutdown call failed");
                
            }
        } 
        
    }
}

But this causes an issue with the "stream" being a moved value.

So how can I shut down the channel right after receiving and doing something with the input?

(I still want to preserve this structure with the loop, since I actually want to receive many messages, and then shut down dependning on the input)


Solution

  • You can try to do a clone of the stream and pass that stream clone to the function you spawn. Then you can call the shutdown inside your spawned function after you have handled the client.

    This way, your original stream variable would remain intact and you can have the behavior you want.

    The reason you get this issue is that the spawned function moves everything it captures due to the move keyword.

        fn main() {
        
        
        let listener = TcpListener::bind("0.0.0.0:8888").expect("Could not bind");
        for stream in listener.incoming() {
            let stream_clone = stream.clone();
    
            match stream {
                Err(e)=> {eprintln!("failed: {}", e)}
                Ok(stream) => {
                    
                    thread::spawn(move || {
                        handle_client(stream_clone).unwrap_or_else(|error| eprintln!("{:?}", error));
                     stream_clone.shutdown(Shutdown::Both).expect("shutdown call failed");
                    });
                }
            }
        }
    }
    

    Also, you would need to change your signature of handle client to fn handle_client(stream: &mut TcpStream)-> Result<(), Error> so it would not also move the cloned stream variable, but burrow as mutable instead.