async-rs / async-std

Async version of the Rust standard library

Home Page:https://async.rs

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Async chat server example connection_writer_loop() might not be necessary

fvacek opened this issue · comments

Documentation

Async chat server example is using connection_writer_loop() task to ensure, that client messages will not be interleaved. https://book.async.rs/tutorial/sending_messages

What if broker_loop() will send client messages back to connection_loop() instead of creating connection_writer_loop() and passing Acr<TcpStram> all around? This can IMO lead to cleaner design, what is always good in tutorial. Also Acr<TcpStram> is not necessary then, because TcpStream never escapes connection_loop(). We can also use let (socket_reader, mut socket_writer) = (&stream, &stream); pattern here.

We can then write something like this:

async fn connection_loop(client_id: i32, broker: Sender<ClientEvent>, stream: TcpStream) -> Result<()> {
    let (socket_reader, mut socket_writer) = (&stream, &stream);

    let reader = BufReader::new(socket_reader);
    let mut lines = reader.lines();

    let name = match lines.next().await {
        None => Err("peer disconnected immediately")?,
        Some(line) => line?,
    };
    broker.send(Event::NewPeer { name: name.clone(), stream: Arc::clone(&stream) }).await // 3
        .unwrap();

    let (peer_sender, peer_receiver) = channel::unbounded::<String>();
    broker
        .send(ClientEvent::NewPeer {
            client_id,
            sender: peer_sender,
        })
        .await
        .unwrap();

    loop {
        select! {
            line = lines.next().fuse() => {
                let line = line?;
                let (dest, msg) = match line.find(':') {
                    None => continue,
                    Some(idx) => (&line[..idx], line[idx + 1 ..].trim()),
                };
                let dest: Vec<String> = dest.split(',').map(|name| name.trim().to_string()).collect();
                let msg: String = msg.to_string();

                broker.send(Event::Message { // 4
                    from: name.clone(),
                    to: dest,
                    msg,
                }).await.unwrap();
            },
            message = peer_receiver.next().fuse() => match message {
                None => {
                    break;
                }
                Some(message) => {
                    socket_writer.send(msg.as_bytes()).await?;
                }
            }
        }
    }
    broker.send(ClientEvent::PeerGone { client_id }).await.unwrap();

    Ok(())
}