Skip to content
This repository was archived by the owner on Sep 5, 2019. It is now read-only.

Commit d7444eb

Browse files
committed
add client
1 parent 6ba647f commit d7444eb

File tree

3 files changed

+219
-2
lines changed

3 files changed

+219
-2
lines changed

src/bin/client.rs

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

tutorial.adoc

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -865,3 +865,76 @@ where
865865
It is not strictly necessary in the current setup, where the broker waits for readers' shutdown anyway.
866866
However, if we add a server-initiated shutdown (for example, kbd:[ctrl+c] handling), this will be a way for the broker to shutdown the writers.
867867
<6> Finally, we close and drain the disconnections channel.
868+
869+
== Implementing a client
870+
871+
Let's now implement the client for the chat.
872+
Because the protocol is line-based, the implementation is pretty straightforward:
873+
874+
* Lines read from stdin should be send over the socket.
875+
* Lines read from the socket should be echoed to stdout.
876+
877+
Unlike the server, the client needs only limited concurrency, as it interacts with only a single user.
878+
For this reason, async doesn't bring a lot of performance benefits in this case.
879+
880+
However, async is still useful for managing concurrency!
881+
Specifically, the client should _simultaneously_ read from stdin and from the socket.
882+
Programming this with threads is cumbersome, especially when implementing clean shutdown.
883+
With async, we can just use the `select!` macro.
884+
885+
[source,rust]
886+
----
887+
#![feature(async_await)]
888+
889+
use std::net::ToSocketAddrs;
890+
891+
use futures::select;
892+
893+
use async_std::{
894+
prelude::*,
895+
net::TcpStream,
896+
task,
897+
io::{stdin, BufReader},
898+
};
899+
900+
type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;
901+
902+
903+
fn main() -> Result<()> {
904+
task::block_on(try_main("127.0.0.1:8080"))
905+
}
906+
907+
async fn try_main(addr: impl ToSocketAddrs) -> Result<()> {
908+
let stream = TcpStream::connect(addr).await?;
909+
let (reader, mut writer) = (&stream, &stream); <1>
910+
let reader = BufReader::new(reader);
911+
let mut lines_from_server = futures::StreamExt::fuse(reader.lines()); <2>
912+
913+
let stdin = BufReader::new(stdin());
914+
let mut lines_from_stdin = futures::StreamExt::fuse(stdin.lines()); <2>
915+
loop {
916+
select! { <3>
917+
line = lines_from_server.next() => match line {
918+
Some(line) => {
919+
let line = line?;
920+
println!("{}", line);
921+
},
922+
None => break,
923+
},
924+
line = lines_from_stdin.next() => match line {
925+
Some(line) => {
926+
let line = line?;
927+
writer.write_all(line.as_bytes()).await?;
928+
writer.write_all(b"\n").await?;
929+
}
930+
None => break,
931+
}
932+
}
933+
}
934+
Ok(())
935+
}
936+
----
937+
938+
<1> Here we split `TcpStream` into read and write halfs: there's `impl AsyncRead for &'_ TcpStream`, just like the one in std.
939+
<2> We crate a steam of lines for both the socket and stdin.
940+
<3> In the main select loop, we print the lines we receive from server and send the lines we read from the console.

tutorial.html

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1560,6 +1560,104 @@ <h2 id="_handling_disconnections">Handling Disconnections</h2>
15601560
</div>
15611561
</div>
15621562
</div>
1563+
<div class="sect1">
1564+
<h2 id="_implementing_a_client">Implementing a client</h2>
1565+
<div class="sectionbody">
1566+
<div class="paragraph">
1567+
<p>Let&#8217;s now implement the client for the chat.
1568+
Because the protocol is line-based, the implementation is pretty straightforward:</p>
1569+
</div>
1570+
<div class="ulist">
1571+
<ul>
1572+
<li>
1573+
<p>Lines read from stdin should be send over the socket.</p>
1574+
</li>
1575+
<li>
1576+
<p>Lines read from the socket should be echoed to stdout.</p>
1577+
</li>
1578+
</ul>
1579+
</div>
1580+
<div class="paragraph">
1581+
<p>Unlike the server, the client needs only limited concurrency, as it interacts with only a single user.
1582+
For this reason, async doesn&#8217;t bring a lot of performance benefits in this case.</p>
1583+
</div>
1584+
<div class="paragraph">
1585+
<p>However, async is still useful for managing concurrency!
1586+
Specifically, the client should <em>simultaneously</em> read from stdin and from the socket.
1587+
Programming this with threads is cumbersome, especially when implementing clean shutdown.
1588+
With async, we can just use the <code>select!</code> macro.</p>
1589+
</div>
1590+
<div class="listingblock">
1591+
<div class="content">
1592+
<pre class="pygments highlight"><code data-lang="rust"><span></span><span class="tok-cp">#![feature(async_await)]</span><span class="tok-w"></span>
1593+
1594+
<span class="tok-k">use</span><span class="tok-w"> </span><span class="tok-n">std</span>::<span class="tok-n">net</span>::<span class="tok-n">ToSocketAddrs</span><span class="tok-p">;</span><span class="tok-w"></span>
1595+
1596+
<span class="tok-k">use</span><span class="tok-w"> </span><span class="tok-n">futures</span>::<span class="tok-n">select</span><span class="tok-p">;</span><span class="tok-w"></span>
1597+
1598+
<span class="tok-k">use</span><span class="tok-w"> </span><span class="tok-n">async_std</span>::<span class="tok-p">{</span><span class="tok-w"></span>
1599+
<span class="tok-w"> </span><span class="tok-n">prelude</span>::<span class="tok-o">*</span><span class="tok-p">,</span><span class="tok-w"></span>
1600+
<span class="tok-w"> </span><span class="tok-n">net</span>::<span class="tok-n">TcpStream</span><span class="tok-p">,</span><span class="tok-w"></span>
1601+
<span class="tok-w"> </span><span class="tok-n">task</span><span class="tok-p">,</span><span class="tok-w"></span>
1602+
<span class="tok-w"> </span><span class="tok-n">io</span>::<span class="tok-p">{</span><span class="tok-n">stdin</span><span class="tok-p">,</span><span class="tok-w"> </span><span class="tok-n">BufReader</span><span class="tok-p">},</span><span class="tok-w"></span>
1603+
<span class="tok-p">};</span><span class="tok-w"></span>
1604+
1605+
<span class="tok-k">type</span> <span class="tok-nb">Result</span><span class="tok-o">&lt;</span><span class="tok-n">T</span><span class="tok-o">&gt;</span><span class="tok-w"> </span><span class="tok-o">=</span><span class="tok-w"> </span><span class="tok-n">std</span>::<span class="tok-n">result</span>::<span class="tok-nb">Result</span><span class="tok-o">&lt;</span><span class="tok-n">T</span><span class="tok-p">,</span><span class="tok-w"> </span><span class="tok-nb">Box</span><span class="tok-o">&lt;</span><span class="tok-n">dyn</span><span class="tok-w"> </span><span class="tok-n">std</span>::<span class="tok-n">error</span>::<span class="tok-n">Error</span><span class="tok-w"> </span><span class="tok-o">+</span><span class="tok-w"> </span><span class="tok-nb">Send</span><span class="tok-w"> </span><span class="tok-o">+</span><span class="tok-w"> </span><span class="tok-nb">Sync</span><span class="tok-o">&gt;&gt;</span><span class="tok-p">;</span><span class="tok-w"></span>
1606+
1607+
1608+
<span class="tok-k">fn</span> <span class="tok-nf">main</span><span class="tok-p">()</span><span class="tok-w"> </span>-&gt; <span class="tok-nb">Result</span><span class="tok-o">&lt;</span><span class="tok-p">()</span><span class="tok-o">&gt;</span><span class="tok-w"> </span><span class="tok-p">{</span><span class="tok-w"></span>
1609+
<span class="tok-w"> </span><span class="tok-n">task</span>::<span class="tok-n">block_on</span><span class="tok-p">(</span><span class="tok-n">try_main</span><span class="tok-p">(</span><span class="tok-s">&quot;127.0.0.1:8080&quot;</span><span class="tok-p">))</span><span class="tok-w"></span>
1610+
<span class="tok-p">}</span><span class="tok-w"></span>
1611+
1612+
<span class="tok-n">async</span><span class="tok-w"> </span><span class="tok-k">fn</span> <span class="tok-nf">try_main</span><span class="tok-p">(</span><span class="tok-n">addr</span>: <span class="tok-nc">impl</span><span class="tok-w"> </span><span class="tok-n">ToSocketAddrs</span><span class="tok-p">)</span><span class="tok-w"> </span>-&gt; <span class="tok-nb">Result</span><span class="tok-o">&lt;</span><span class="tok-p">()</span><span class="tok-o">&gt;</span><span class="tok-w"> </span><span class="tok-p">{</span><span class="tok-w"></span>
1613+
<span class="tok-w"> </span><span class="tok-kd">let</span><span class="tok-w"> </span><span class="tok-n">stream</span><span class="tok-w"> </span><span class="tok-o">=</span><span class="tok-w"> </span><span class="tok-n">TcpStream</span>::<span class="tok-n">connect</span><span class="tok-p">(</span><span class="tok-n">addr</span><span class="tok-p">).</span><span class="tok-n">await</span><span class="tok-o">?</span><span class="tok-p">;</span><span class="tok-w"></span>
1614+
<span class="tok-w"> </span><span class="tok-kd">let</span><span class="tok-w"> </span><span class="tok-p">(</span><span class="tok-n">reader</span><span class="tok-p">,</span><span class="tok-w"> </span><span class="tok-k">mut</span><span class="tok-w"> </span><span class="tok-n">writer</span><span class="tok-p">)</span><span class="tok-w"> </span><span class="tok-o">=</span><span class="tok-w"> </span><span class="tok-p">(</span><span class="tok-o">&amp;</span><span class="tok-n">stream</span><span class="tok-p">,</span><span class="tok-w"> </span><span class="tok-o">&amp;</span><span class="tok-n">stream</span><span class="tok-p">);</span><span class="tok-w"> </span><i class="conum" data-value="1"></i><b>(1)</b>
1615+
<span class="tok-w"> </span><span class="tok-kd">let</span><span class="tok-w"> </span><span class="tok-n">reader</span><span class="tok-w"> </span><span class="tok-o">=</span><span class="tok-w"> </span><span class="tok-n">BufReader</span>::<span class="tok-n">new</span><span class="tok-p">(</span><span class="tok-n">reader</span><span class="tok-p">);</span><span class="tok-w"></span>
1616+
<span class="tok-w"> </span><span class="tok-kd">let</span><span class="tok-w"> </span><span class="tok-k">mut</span><span class="tok-w"> </span><span class="tok-n">lines_from_server</span><span class="tok-w"> </span><span class="tok-o">=</span><span class="tok-w"> </span><span class="tok-n">futures</span>::<span class="tok-n">StreamExt</span>::<span class="tok-n">fuse</span><span class="tok-p">(</span><span class="tok-n">reader</span><span class="tok-p">.</span><span class="tok-n">lines</span><span class="tok-p">());</span><span class="tok-w"> </span><i class="conum" data-value="2"></i><b>(2)</b>
1617+
1618+
<span class="tok-w"> </span><span class="tok-kd">let</span><span class="tok-w"> </span><span class="tok-n">stdin</span><span class="tok-w"> </span><span class="tok-o">=</span><span class="tok-w"> </span><span class="tok-n">BufReader</span>::<span class="tok-n">new</span><span class="tok-p">(</span><span class="tok-n">stdin</span><span class="tok-p">());</span><span class="tok-w"></span>
1619+
<span class="tok-w"> </span><span class="tok-kd">let</span><span class="tok-w"> </span><span class="tok-k">mut</span><span class="tok-w"> </span><span class="tok-n">lines_from_stdin</span><span class="tok-w"> </span><span class="tok-o">=</span><span class="tok-w"> </span><span class="tok-n">futures</span>::<span class="tok-n">StreamExt</span>::<span class="tok-n">fuse</span><span class="tok-p">(</span><span class="tok-n">stdin</span><span class="tok-p">.</span><span class="tok-n">lines</span><span class="tok-p">());</span><span class="tok-w"> </span><i class="conum" data-value="2"></i><b>(2)</b>
1620+
<span class="tok-w"> </span><span class="tok-k">loop</span><span class="tok-w"> </span><span class="tok-p">{</span><span class="tok-w"></span>
1621+
<span class="tok-w"> </span><span class="tok-n">select</span><span class="tok-o">!</span><span class="tok-w"> </span><span class="tok-p">{</span><span class="tok-w"> </span><i class="conum" data-value="3"></i><b>(3)</b>
1622+
<span class="tok-w"> </span><span class="tok-n">line</span><span class="tok-w"> </span><span class="tok-o">=</span><span class="tok-w"> </span><span class="tok-n">lines_from_server</span><span class="tok-p">.</span><span class="tok-n">next</span><span class="tok-p">()</span><span class="tok-w"> </span><span class="tok-o">=&gt;</span><span class="tok-w"> </span><span class="tok-k">match</span><span class="tok-w"> </span><span class="tok-n">line</span><span class="tok-w"> </span><span class="tok-p">{</span><span class="tok-w"></span>
1623+
<span class="tok-w"> </span><span class="tok-nb">Some</span><span class="tok-p">(</span><span class="tok-n">line</span><span class="tok-p">)</span><span class="tok-w"> </span><span class="tok-o">=&gt;</span><span class="tok-w"> </span><span class="tok-p">{</span><span class="tok-w"></span>
1624+
<span class="tok-w"> </span><span class="tok-kd">let</span><span class="tok-w"> </span><span class="tok-n">line</span><span class="tok-w"> </span><span class="tok-o">=</span><span class="tok-w"> </span><span class="tok-n">line</span><span class="tok-o">?</span><span class="tok-p">;</span><span class="tok-w"></span>
1625+
<span class="tok-w"> </span><span class="tok-n">println</span><span class="tok-o">!</span><span class="tok-p">(</span><span class="tok-s">&quot;{}&quot;</span><span class="tok-p">,</span><span class="tok-w"> </span><span class="tok-n">line</span><span class="tok-p">);</span><span class="tok-w"></span>
1626+
<span class="tok-w"> </span><span class="tok-p">},</span><span class="tok-w"></span>
1627+
<span class="tok-w"> </span><span class="tok-nb">None</span><span class="tok-w"> </span><span class="tok-o">=&gt;</span><span class="tok-w"> </span><span class="tok-k">break</span><span class="tok-p">,</span><span class="tok-w"></span>
1628+
<span class="tok-w"> </span><span class="tok-p">},</span><span class="tok-w"></span>
1629+
<span class="tok-w"> </span><span class="tok-n">line</span><span class="tok-w"> </span><span class="tok-o">=</span><span class="tok-w"> </span><span class="tok-n">lines_from_stdin</span><span class="tok-p">.</span><span class="tok-n">next</span><span class="tok-p">()</span><span class="tok-w"> </span><span class="tok-o">=&gt;</span><span class="tok-w"> </span><span class="tok-k">match</span><span class="tok-w"> </span><span class="tok-n">line</span><span class="tok-w"> </span><span class="tok-p">{</span><span class="tok-w"></span>
1630+
<span class="tok-w"> </span><span class="tok-nb">Some</span><span class="tok-p">(</span><span class="tok-n">line</span><span class="tok-p">)</span><span class="tok-w"> </span><span class="tok-o">=&gt;</span><span class="tok-w"> </span><span class="tok-p">{</span><span class="tok-w"></span>
1631+
<span class="tok-w"> </span><span class="tok-kd">let</span><span class="tok-w"> </span><span class="tok-n">line</span><span class="tok-w"> </span><span class="tok-o">=</span><span class="tok-w"> </span><span class="tok-n">line</span><span class="tok-o">?</span><span class="tok-p">;</span><span class="tok-w"></span>
1632+
<span class="tok-w"> </span><span class="tok-n">writer</span><span class="tok-p">.</span><span class="tok-n">write_all</span><span class="tok-p">(</span><span class="tok-n">line</span><span class="tok-p">.</span><span class="tok-n">as_bytes</span><span class="tok-p">()).</span><span class="tok-n">await</span><span class="tok-o">?</span><span class="tok-p">;</span><span class="tok-w"></span>
1633+
<span class="tok-w"> </span><span class="tok-n">writer</span><span class="tok-p">.</span><span class="tok-n">write_all</span><span class="tok-p">(</span><span class="tok-s">b&quot;</span><span class="tok-se">\n</span><span class="tok-s">&quot;</span><span class="tok-p">).</span><span class="tok-n">await</span><span class="tok-o">?</span><span class="tok-p">;</span><span class="tok-w"></span>
1634+
<span class="tok-w"> </span><span class="tok-p">}</span><span class="tok-w"></span>
1635+
<span class="tok-w"> </span><span class="tok-nb">None</span><span class="tok-w"> </span><span class="tok-o">=&gt;</span><span class="tok-w"> </span><span class="tok-k">break</span><span class="tok-p">,</span><span class="tok-w"></span>
1636+
<span class="tok-w"> </span><span class="tok-p">}</span><span class="tok-w"></span>
1637+
<span class="tok-w"> </span><span class="tok-p">}</span><span class="tok-w"></span>
1638+
<span class="tok-w"> </span><span class="tok-p">}</span><span class="tok-w"></span>
1639+
<span class="tok-w"> </span><span class="tok-nb">Ok</span><span class="tok-p">(())</span><span class="tok-w"></span>
1640+
<span class="tok-p">}</span><span class="tok-w"></span></code></pre>
1641+
</div>
1642+
</div>
1643+
<div class="colist arabic">
1644+
<table>
1645+
<tr>
1646+
<td><i class="conum" data-value="1"></i><b>1</b></td>
1647+
<td>Here we split <code>TcpStream</code> into read and write halfs: there&#8217;s <code>impl AsyncRead for &amp;'_ TcpStream</code>, just like the one in std.</td>
1648+
</tr>
1649+
<tr>
1650+
<td><i class="conum" data-value="2"></i><b>2</b></td>
1651+
<td>We crate a steam of lines for both the socket and stdin.</td>
1652+
</tr>
1653+
<tr>
1654+
<td><i class="conum" data-value="3"></i><b>3</b></td>
1655+
<td>In the main select loop, we print the lines we receive from server and send the lines we read from the console.</td>
1656+
</tr>
1657+
</table>
1658+
</div>
1659+
</div>
1660+
</div>
15631661
</div>
15641662
<div id="footer">
15651663
<div id="footer-text">

0 commit comments

Comments
 (0)