2
0
Fork 1
mirror of https://github.com/async-rs/async-std.git synced 2025-04-14 12:26:47 +00:00
async-std/docs/src/tutorial/sending_messages.md
bors[bot] 33ff41df48
Merge #224
224: Re-export IO traits from futures r=stjepang a=stjepang

Sorry for the big PR!

Instead of providing our own traits `async_std::io::{Read, Write, Seek, BufRead}`, we now re-export `futures::io::{AsyncRead, AsyncWrite, AsyncSeek, AsyncRead}`. While re-exporting we rename them to strip away the "Async" prefix.

The documentation will display the contents of the original traits from the `futures` crate together with our own extension methods. There's a note in the docs saying the extenion methods become available only when `async_std::prelude::*` is imported.

Our extension traits are re-exported into the prelude, but are marked with `#[doc(hidden)]` so they're completely invisible to users.

The benefit of this is that people can now implement traits from `async_std::io` for their types and stay compatible with `futures`. This will also simplify some trait bounds in our APIs - for example, things like `where Self: futures_io::AsyncRead`.

At the same time, I cleaned up some trait bounds in our stream interfaces, but haven't otherwise fiddled with them much.

I intend to follow up with another PR doing the same change for `Stream` so that we re-export the stream trait from `futures`.

Co-authored-by: Stjepan Glavina <stjepang@gmail.com>
2019-09-22 13:50:53 +00:00

2 KiB

Sending Messages

Now it's time to implement the other half -- sending messages. A most obvious way to implement sending is to give each connection_loop access to the write half of TcpStream of each other clients. That way, a client can directly .write_all a message to recipients. However, this would be wrong: if Alice sends bob: foo, and Charley sends bob: bar, Bob might actually receive fobaor. Sending a message over a socket might require several syscalls, so two concurrent .write_all's might interfere with each other!

As a rule of thumb, only a single task should write to each TcpStream. So let's create a connection_writer_loop task which receives messages over a channel and writes them to the socket. This task would be the point of serialization of messages. if Alice and Charley send two messages to Bob at the same time, Bob will see the messages in the same order as they arrive in the channel.

# extern crate async_std;
# extern crate futures_channel;
# extern crate futures_util;
# use async_std::{
#     net::TcpStream,
#     prelude::*,
# };
use futures_channel::mpsc; // 1
use futures_util::sink::SinkExt;
use std::sync::Arc;

# type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;
type Sender<T> = mpsc::UnboundedSender<T>; // 2
type Receiver<T> = mpsc::UnboundedReceiver<T>;

async fn connection_writer_loop(
    mut messages: Receiver<String>,
    stream: Arc<TcpStream>, // 3
) -> Result<()> {
    let mut stream = &*stream;
    while let Some(msg) = messages.next().await {
        stream.write_all(msg.as_bytes()).await?;
    }
    Ok(())
}
  1. We will use channels from the futures crate.
  2. For simplicity, we will use unbounded channels, and won't be discussing backpressure in this tutorial.
  3. As connection_loop and connection_writer_loop share the same TcpStream, we need to put it into an Arc. Note that because client only reads from the stream and connection_writer_loop only writes to the stream, we don't get a race here.