Search code examples
rusttcpiobuffer

Why is my Server appears to only able works whenever my `BufWriter` and `BufReader` are in a separate function?


I have this simple code that handles the request over a stream.

fn handle_request(stream: TcpStream){

    // Create a buffer reader
    let mut buf_reader = BufReader::new(&stream);

    // Create a buffer to store the data
    let mut buffer = Vec::<u8>::new();

    // Read the data into the buffer
    buf_reader.read_until(b'\n', &mut buffer).unwrap(); // can be replaced by `read_line()`

    // Convert the buffer to a string
    let str_buffer = String::from_utf8(buffer.clone()).unwrap();

    // Handle the request
    respond_to_client(&str_buffer, &stream); // Assume that this writes "VALUE\n" to the stream
}

and I have this code in my test.rs to test this functionality:

Code 00

fn main(){
    let mut stream = TcpStream::connect("127.0.0.1:9073").unwrap(); // server address

    let mut my_buf_writer = BufWriter::new(&stream);

    my_buf_writer.write(b"GET|1|ALL\n").unwrap();

    let mut my_buf_reader = BufReader::new(&stream);

    let mut buffer = String::new();

    println!("Reading line");
    my_buf_reader.read_line(&mut buffer).unwrap(); // Doesn't stop

    println!("{}", buffer);

}

However, for some reason, this don't work at all UNLESS, I separate my writer and reader in a separte function like this:

Code 01

fn main(){
    let mut stream = TcpStream::connect("127.0.0.1:9073").unwrap(); // server address
    write_in_database(&stream);
    read_from_database(&stream)
}

fn write_in_database(stream: &TcpStream){

    let mut my_buf_writer = BufWriter::new(stream);

    my_buf_writer.write(b"GET|1|ALL\n").unwrap();
}

fn read_from_database(stream: &TcpStream){

    let mut my_buf_reader = BufReader::new(stream);

    let mut buffer = String::new();

    println!("Reading line");
    my_buf_reader.read_line(&mut buffer).unwrap(); // Now for some reason, it stops.

    println!("{}", buffer);
}

I don't really get what's the difference between the two. Code 00 doesn't stop reading the stream even though the returning value has \n UNLESS I restructure my code into Code 01 then it would work. Can someone give me an explanation?


Solution

  • The problem is that the data doesn't get written to the TcpStream until you flush it, that happens automatically when the BufWriter is dropped. Unless you do so the the server is waiting for the client to start talking, and the client is waiting for the server to respond to the unsent message in other words you're in a deadlock.

    In addition to the two solutions already in your post and the comments simply ensuring that the data has ben sent with a flush also works:

    
    use std::net::TcpStream;
    use std::io::{BufReader, BufWriter, Write, BufRead};
    fn main() {
        let mut stream = TcpStream::connect("127.0.0.1:9073").unwrap();
        let mut my_buf_writer = BufWriter::new(&stream);
    
        writeln!(my_buf_writer, "GET|1|ALL").unwrap();
        my_buf_writer.flush().unwrap();
    
        let mut my_buf_reader = BufReader::new(&stream);
    
        let mut buffer = String::new();
    
        println!("Reading line");
        my_buf_reader.read_line(&mut buffer).unwrap(); // Doesn't stop
    
        println!("{}", buffer);
    }
    

    Tip: a plain nc -l 9073 as "server" helps debug such problems, here you see the "Reading line" message on the server console before it sent any data.