Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 75a4ba8

Browse files
authored
Merge pull request #144 from matklad/move-a-chat
move a-chat tutorial's code to this repo
2 parents 238a3c8 + 5b96fa9 commit 75a4ba8

File tree

4 files changed

+244
-2
lines changed

4 files changed

+244
-2
lines changed

‎docs/src/tutorial/index.md‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,4 @@ How do you distribute the messages?
88

99
In this tutorial, we will show you how to write one in `async-std`.
1010

11-
You can also find the tutorial in [our repository](https://github.com/async-rs/a-chat).
12-
11+
You can also find the tutorial in [our repository](https://github.com/async-rs/async-std/blob/master/examples/a-chat).

‎examples/a-chat/client.rs‎

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
use futures::select;
2+
use futures::FutureExt;
3+
4+
use async_std::{
5+
io::{stdin, BufReader},
6+
net::{TcpStream, ToSocketAddrs},
7+
prelude::*,
8+
task,
9+
};
10+
11+
type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;
12+
13+
pub(crate) fn main() -> Result<()> {
14+
task::block_on(try_main("127.0.0.1:8080"))
15+
}
16+
17+
async fn try_main(addr: impl ToSocketAddrs) -> Result<()> {
18+
let stream = TcpStream::connect(addr).await?;
19+
let (reader, mut writer) = (&stream, &stream);
20+
let reader = BufReader::new(reader);
21+
let mut lines_from_server = futures::StreamExt::fuse(reader.lines());
22+
23+
let stdin = BufReader::new(stdin());
24+
let mut lines_from_stdin = futures::StreamExt::fuse(stdin.lines());
25+
loop {
26+
select! {
27+
line = lines_from_server.next().fuse() => match line {
28+
Some(line) => {
29+
let line = line?;
30+
println!("{}", line);
31+
},
32+
None => break,
33+
},
34+
line = lines_from_stdin.next().fuse() => match line {
35+
Some(line) => {
36+
let line = line?;
37+
writer.write_all(line.as_bytes()).await?;
38+
writer.write_all(b"\n").await?;
39+
}
40+
None => break,
41+
}
42+
}
43+
}
44+
Ok(())
45+
}

‎examples/a-chat/main.rs‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
mod client;
2+
mod server;
3+
4+
type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;
5+
6+
fn main() -> Result<()> {
7+
let mut args = std::env::args();
8+
match (args.nth(1).as_ref().map(String::as_str), args.next()) {
9+
(Some("client"), None) => client::main(),
10+
(Some("server"), None) => server::main(),
11+
_ => Err("Usage: a-chat [client|server]")?,
12+
}
13+
}

‎examples/a-chat/server.rs‎

Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
use std::{
2+
collections::hash_map::{Entry, HashMap},
3+
sync::Arc,
4+
};
5+
6+
use futures::{channel::mpsc, select, FutureExt, SinkExt};
7+
8+
use async_std::{
9+
io::BufReader,
10+
net::{TcpListener, TcpStream, ToSocketAddrs},
11+
prelude::*,
12+
task,
13+
};
14+
15+
type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;
16+
type Sender<T> = mpsc::UnboundedSender<T>;
17+
type Receiver<T> = mpsc::UnboundedReceiver<T>;
18+
19+
#[derive(Debug)]
20+
enum Void {}
21+
22+
pub(crate) fn main() -> Result<()> {
23+
task::block_on(accept_loop("127.0.0.1:8080"))
24+
}
25+
26+
async fn accept_loop(addr: impl ToSocketAddrs) -> Result<()> {
27+
let listener = TcpListener::bind(addr).await?;
28+
29+
let (broker_sender, broker_receiver) = mpsc::unbounded();
30+
let broker = task::spawn(broker_loop(broker_receiver));
31+
let mut incoming = listener.incoming();
32+
while let Some(stream) = incoming.next().await {
33+
let stream = stream?;
34+
println!("Accepting from: {}", stream.peer_addr()?);
35+
spawn_and_log_error(connection_loop(broker_sender.clone(), stream));
36+
}
37+
drop(broker_sender);
38+
broker.await;
39+
Ok(())
40+
}
41+
42+
async fn connection_loop(mut broker: Sender<Event>, stream: TcpStream) -> Result<()> {
43+
let stream = Arc::new(stream);
44+
let reader = BufReader::new(&*stream);
45+
let mut lines = reader.lines();
46+
47+
let name = match lines.next().await {
48+
None => Err("peer disconnected immediately")?,
49+
Some(line) => line?,
50+
};
51+
let (_shutdown_sender, shutdown_receiver) = mpsc::unbounded::<Void>();
52+
broker
53+
.send(Event::NewPeer {
54+
name: name.clone(),
55+
stream: Arc::clone(&stream),
56+
shutdown: shutdown_receiver,
57+
})
58+
.await
59+
.unwrap();
60+
61+
while let Some(line) = lines.next().await {
62+
let line = line?;
63+
let (dest, msg) = match line.find(':') {
64+
None => continue,
65+
Some(idx) => (&line[..idx], line[idx + 1..].trim()),
66+
};
67+
let dest: Vec<String> = dest
68+
.split(',')
69+
.map(|name| name.trim().to_string())
70+
.collect();
71+
let msg: String = msg.trim().to_string();
72+
73+
broker
74+
.send(Event::Message {
75+
from: name.clone(),
76+
to: dest,
77+
msg,
78+
})
79+
.await
80+
.unwrap();
81+
}
82+
83+
Ok(())
84+
}
85+
86+
async fn connection_writer_loop(
87+
messages: &mut Receiver<String>,
88+
stream: Arc<TcpStream>,
89+
mut shutdown: Receiver<Void>,
90+
) -> Result<()> {
91+
let mut stream = &*stream;
92+
loop {
93+
select! {
94+
msg = messages.next().fuse() => match msg {
95+
Some(msg) => stream.write_all(msg.as_bytes()).await?,
96+
None => break,
97+
},
98+
void = shutdown.next().fuse() => match void {
99+
Some(void) => match void {},
100+
None => break,
101+
}
102+
}
103+
}
104+
Ok(())
105+
}
106+
107+
#[derive(Debug)]
108+
enum Event {
109+
NewPeer {
110+
name: String,
111+
stream: Arc<TcpStream>,
112+
shutdown: Receiver<Void>,
113+
},
114+
Message {
115+
from: String,
116+
to: Vec<String>,
117+
msg: String,
118+
},
119+
}
120+
121+
async fn broker_loop(mut events: Receiver<Event>) {
122+
let (disconnect_sender, mut disconnect_receiver) =
123+
mpsc::unbounded::<(String, Receiver<String>)>();
124+
let mut peers: HashMap<String, Sender<String>> = HashMap::new();
125+
126+
loop {
127+
let event = select! {
128+
event = events.next().fuse() => match event {
129+
None => break,
130+
Some(event) => event,
131+
},
132+
disconnect = disconnect_receiver.next().fuse() => {
133+
let (name, _pending_messages) = disconnect.unwrap();
134+
assert!(peers.remove(&name).is_some());
135+
continue;
136+
},
137+
};
138+
match event {
139+
Event::Message { from, to, msg } => {
140+
for addr in to {
141+
if let Some(peer) = peers.get_mut(&addr) {
142+
peer.send(format!("from {}: {}\n", from, msg))
143+
.await
144+
.unwrap()
145+
}
146+
}
147+
}
148+
Event::NewPeer {
149+
name,
150+
stream,
151+
shutdown,
152+
} => match peers.entry(name.clone()) {
153+
Entry::Occupied(..) => (),
154+
Entry::Vacant(entry) => {
155+
let (client_sender, mut client_receiver) = mpsc::unbounded();
156+
entry.insert(client_sender);
157+
let mut disconnect_sender = disconnect_sender.clone();
158+
spawn_and_log_error(async move {
159+
let res =
160+
connection_writer_loop(&mut client_receiver, stream, shutdown).await;
161+
disconnect_sender
162+
.send((name, client_receiver))
163+
.await
164+
.unwrap();
165+
res
166+
});
167+
}
168+
},
169+
}
170+
}
171+
drop(peers);
172+
drop(disconnect_sender);
173+
while let Some((_name, _pending_messages)) = disconnect_receiver.next().await {}
174+
}
175+
176+
fn spawn_and_log_error<F>(fut: F) -> task::JoinHandle<()>
177+
where
178+
F: Future<Output = Result<()>> + Send + 'static,
179+
{
180+
task::spawn(async move {
181+
if let Err(e) = fut.await {
182+
eprintln!("{}", e)
183+
}
184+
})
185+
}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /