rusttcp

TcpStream blocked on read_to_string


The code

I have a client (a.rs) and a server (b.rs):

// a.rs
use std::{
    io::{Read, Write},
    net::TcpListener,
};

fn main() {
    let listener = TcpListener::bind("0.0.0.0:6400").unwrap();
    for stream in listener.incoming() {
        let mut stream = stream.unwrap();
        let mut buf = String::new();
        println!("Waiting for query...");
        stream.read_to_string(&mut buf).unwrap();
        println!("> Received query: {}", buf);

        // Compute something based on query
        let query_res = String::from("42");

        println!("Writing...");
        stream.write(query_res.as_bytes()).unwrap();

        println!("> Done");
    }
}
// b.rs
use std::{io::{Read, Write}, net::TcpStream, thread, time::Duration};

fn main() {
    let mut stream = TcpStream::connect("127.0.0.1:6400").unwrap();

    println!("Sending query...");
    stream
        .write(String::from("What is the best number?").as_bytes())
        .unwrap();
    println!("> Done");

    println!("Receiving...");
    let mut buf = String::new();
    stream.read_to_string(&mut buf).unwrap();
    println!("> Received: {}", buf);
}

What it's supposed to:

Terminal A:

$ cargo run --bin a
Waiting for query...

Terminal B:

$ cargo run --bin b
Sending query...
> Done
Receiving...
> Received: 42

Terminal A:

> Received query: What is the best number?
Writing...
> Done

Instead, I got:

Terminal A:

$ cargo run --bin a
Waiting for query...

Terminal B:

$ cargo run --bin b
Sending query...
> Done
Receiving...

And both terminals are blocked. Why and how to solve this?


Solution

  • read_to_string reads into String until EOF which will not happen until you close the stream from the writer side.

    What you can do is write the query as line using writeln!:

    println!("Sending query...");
    writeln!(stream, "What is the best number?").unwrap();
    println!("> Done");
    

    and create a buffered reader to read a line:

    let mut stream = stream.unwrap();
    let mut reader = BufReader::new(&stream);
    let mut buf = String::new();
    println!("Waiting for query...");
    reader.read_line(&mut buf).unwrap();
    println!("> Received query: {}", buf);