简体   繁体   中英

Read/write problem in async tokio application (beginner)

I'm new to network programming and thread in Rust so I may be missing something obvious here. I've been following along with this trying to build a simple chat application. Only, he does it with the standard library and I'm trying to do it with tokio. The functionality is very simple: Client sends a message to Server, Server acknowledges it and sends it back to the Client. Here's my code for the client and server, stripped down as much as I can:

server.rs

#[tokio::main]
async fn main() {
    let server = TcpListener::bind("127.0.0.1:7878").await.unwrap();      
    let mut clients = vec![];
    let (tx, mut rx) = mpsc::channel(32);
    
    loop {
        if let Ok((socket, addr)) = server.accept().await {
            let tx = tx.clone();
            let (mut reader, writer) = split(socket);
            clients.push(writer);

            tokio::spawn(async move {
                loop {
                    let mut buffer = vec![0; 1024];

                    reader.read(&mut buffer).await.unwrap();
                    //get message written by the client and print it
                    //then transmit it on the channel
                    let msg = buffer.into_iter().take_while(|&x| x != 0).collect::<Vec<_>>();
                    let msg = String::from_utf8(msg).expect("Invalid utf8 message");
                    println!("{}: {:?}", addr, msg);

                    match tx.send(msg).await {
                        Ok(_) => { ()}
                        Err(_) => { println!("Error");}
                    }
                }

            });
        }

        //write each message received back to its client
        if let Some(msg) = rx.recv().await { 
            clients = clients.into_iter().filter_map(|mut x| {
                println!("writing: {:?}", &msg);
                x.write(&msg.clone().into_bytes());
                Some(x)
            }).collect::<Vec<_>>();
        }
        
    }
}

client.rs

#[tokio::main]
async fn main() {
    let client = TcpStream::connect("127.0.0.1:7878").await.unwrap();
    let (tx, mut rx) = mpsc::channel::<String>(32);

    tokio::spawn(async move {
        loop {
            let mut buffer = vec![0; 1024];

            // get message sent by the server and print it
            match client.try_read(&mut buffer) {
                Ok(_) => { 
                    let msg = buffer.into_iter().take_while(|&x| x != 0).collect::<Vec<_>>();
                    println!("Received from server: {:?}", msg); 
                }
                Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => {
                    ()
                }
                Err(_) => {
                    println!("Connection with server was severed");
                    break;
                }
            }
            
            // get message transmitted from user input loop
            // then write it to the server
            match rx.try_recv() {
                Ok(message) => {
                    let mut buffer = message.clone().into_bytes();
                    buffer.resize(1024, 0);
                    match client.try_write(&buffer) {
                        Ok(_) => { println!("Write successful");}
                        Err(_) => { println!("Write error");}
                    }
                }
                Err(TryRecvError::Empty) => (), 
                _ => break

            }
        }
    } );
// user input loop here
// takes user message and transmits it on the channel
    
}

Sending to the server works fine, and the server appears to be successfully writing as indicated by its output:

127.0.0.1:55346: "test message"
writing: "test message"

The issue is the client never reads back the message from the server, instead getting WouldBlock errors every time it hits the match client.try_read(&mut buffer) block.

If I stop the server while keeping the client running, the client is suddenly flooded with successful reads of empty messages:

Received from server: []
Received from server: []
Received from server: []
Received from server: []
Received from server: []
Received from server: []
Received from server: []
Received from server: []
...

Can anyone tell me what's going on?

Here's what happens in your server:

  • Wait for a client to connect.
  • When the client is connected, spawn a background task to receive from the client.
  • Try to read from the channel, since it is very unlikely that the client has already sent anything at this point the channel is empty.
  • Loop → wait for another client to connect.

While waiting for another client, the background task receives the message from the first client and sends it to the channel, but the main task is blocked waiting for another client and never tries to read again from the channel.

Easiest way to get it to work is to get rid of the channel in the server and simply echo the message from the spawned task.

Another solution is to spawn an independent task to process the channel and write to the clients.


As for what happens when you kill the server: once the connection is lost attempting to read from the socket does not return an error but instead returns an empty buffer.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM