Skip to content

Commit 1434b32

Browse files
authored
examples: improve echo example consistency (#7256)
1 parent 159a3b2 commit 1434b32

10 files changed

+184
-165
lines changed

examples/Cargo.toml

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,16 +33,20 @@ name = "chat"
3333
path = "chat.rs"
3434

3535
[[example]]
36-
name = "connect"
37-
path = "connect.rs"
36+
name = "connect-tcp"
37+
path = "connect-tcp.rs"
3838

3939
[[example]]
40-
name = "echo-udp"
41-
path = "echo-udp.rs"
40+
name = "connect-udp"
41+
path = "connect-udp.rs"
42+
43+
[[example]]
44+
name = "echo-tcp"
45+
path = "echo-tcp.rs"
4246

4347
[[example]]
44-
name = "echo"
45-
path = "echo.rs"
48+
name = "echo-udp"
49+
path = "echo-udp.rs"
4650

4751
[[example]]
4852
name = "hello_world"

examples/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ cargo run --example $name
1010
```
1111

1212
A good starting point for the examples would be [`hello_world`](hello_world.rs)
13-
and [`echo`](echo.rs). Additionally [the tokio website][tokioweb] contains
13+
and [`echo-tcp`](echo-tcp.rs). Additionally [the tokio website][tokioweb] contains
1414
additional guides for some of the examples.
1515

1616
For a larger "real world" example, see the [`mini-redis`][redis] repository.

examples/connect-tcp.rs

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
//! An example of hooking up stdin/stdout to a TCP stream.
2+
//!
3+
//! This example will connect to a socket address specified in the argument list
4+
//! and then forward all data read on stdin to the server, printing out all data
5+
//! received on stdout. Each line entered on stdin will be translated to a TCP
6+
//! packet which is then sent to the remote address.
7+
//!
8+
//! Note that this is not currently optimized for performance, especially
9+
//! around buffer management. Rather it's intended to show an example of
10+
//! working with a client.
11+
//!
12+
//! This example can be quite useful when interacting with the other examples in
13+
//! this repository! Many of them recommend running this as a simple "hook up
14+
//! stdin/stdout to a server" to get up and running.
15+
16+
#![warn(rust_2018_idioms)]
17+
18+
use tokio::io::{stdin, stdout};
19+
use tokio::net::TcpStream;
20+
use tokio_util::codec::{BytesCodec, FramedRead, FramedWrite};
21+
22+
use bytes::Bytes;
23+
use futures::{future, Sink, SinkExt, Stream, StreamExt};
24+
use std::env;
25+
use std::error::Error;
26+
use std::net::SocketAddr;
27+
28+
#[tokio::main]
29+
async fn main() -> Result<(), Box<dyn Error>> {
30+
// Parse what address we're going to connect to
31+
let args = env::args().skip(1).collect::<Vec<_>>();
32+
let addr = args
33+
.first()
34+
.ok_or("this program requires at least one argument")?;
35+
let addr = addr.parse::<SocketAddr>()?;
36+
37+
let stdin = FramedRead::new(stdin(), BytesCodec::new());
38+
let stdin = stdin.map(|i| i.map(|bytes| bytes.freeze()));
39+
let stdout = FramedWrite::new(stdout(), BytesCodec::new());
40+
41+
connect(&addr, stdin, stdout).await?;
42+
43+
Ok(())
44+
}
45+
46+
pub async fn connect(
47+
addr: &SocketAddr,
48+
mut stdin: impl Stream<Item = Result<Bytes, std::io::Error>> + Unpin,
49+
mut stdout: impl Sink<Bytes, Error = std::io::Error> + Unpin,
50+
) -> Result<(), Box<dyn Error>> {
51+
let mut stream = TcpStream::connect(addr).await?;
52+
let (r, w) = stream.split();
53+
let mut sink = FramedWrite::new(w, BytesCodec::new());
54+
// filter map Result<BytesMut, Error> stream into just a Bytes stream to match stdout Sink
55+
// on the event of an Error, log the error and end the stream
56+
let mut stream = FramedRead::new(r, BytesCodec::new())
57+
.filter_map(|i| match i {
58+
//BytesMut into Bytes
59+
Ok(i) => future::ready(Some(i.freeze())),
60+
Err(e) => {
61+
println!("failed to read from socket; error={e}");
62+
future::ready(None)
63+
}
64+
})
65+
.map(Ok);
66+
67+
match future::join(sink.send_all(&mut stdin), stdout.send_all(&mut stream)).await {
68+
(Err(e), _) | (_, Err(e)) => Err(e.into()),
69+
_ => Ok(()),
70+
}
71+
}

examples/connect-udp.rs

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
//! An example of hooking up stdin/stdout to a UDP stream.
2+
//!
3+
//! This example will connect to a socket address specified in the argument list
4+
//! and then forward all data read on stdin to the server, printing out all data
5+
//! received on stdout. Each line entered on stdin will be translated to a UDP
6+
//! packet which is then sent to the remote address.
7+
//!
8+
//! Note that this is not currently optimized for performance, especially
9+
//! around buffer management. Rather it's intended to show an example of
10+
//! working with a client.
11+
//!
12+
//! This example can be quite useful when interacting with the other examples in
13+
//! this repository! Many of them recommend running this as a simple "hook up
14+
//! stdin/stdout to a server" to get up and running.
15+
16+
#![warn(rust_2018_idioms)]
17+
18+
use tokio::io::{stdin, stdout};
19+
use tokio::net::UdpSocket;
20+
use tokio_util::codec::{BytesCodec, FramedRead, FramedWrite};
21+
22+
use bytes::Bytes;
23+
use futures::{Sink, SinkExt, Stream, StreamExt};
24+
use std::env;
25+
use std::error::Error;
26+
use std::net::SocketAddr;
27+
28+
#[tokio::main]
29+
async fn main() -> Result<(), Box<dyn Error>> {
30+
// Parse what address we're going to connect to
31+
let args = env::args().skip(1).collect::<Vec<_>>();
32+
let addr = args
33+
.first()
34+
.ok_or("this program requires at least one argument")?;
35+
let addr = addr.parse::<SocketAddr>()?;
36+
37+
let stdin = FramedRead::new(stdin(), BytesCodec::new());
38+
let stdin = stdin.map(|i| i.map(|bytes| bytes.freeze()));
39+
let stdout = FramedWrite::new(stdout(), BytesCodec::new());
40+
41+
connect(&addr, stdin, stdout).await?;
42+
43+
Ok(())
44+
}
45+
46+
pub async fn connect(
47+
addr: &SocketAddr,
48+
stdin: impl Stream<Item = Result<Bytes, std::io::Error>> + Unpin,
49+
stdout: impl Sink<Bytes, Error = std::io::Error> + Unpin,
50+
) -> Result<(), Box<dyn Error>> {
51+
// We'll bind our UDP socket to a local IP/port, but for now we
52+
// basically let the OS pick both of those.
53+
let bind_addr = if addr.ip().is_ipv4() {
54+
"0.0.0.0:0"
55+
} else {
56+
"[::]:0"
57+
};
58+
59+
let socket = UdpSocket::bind(&bind_addr).await?;
60+
socket.connect(addr).await?;
61+
62+
tokio::try_join!(send(stdin, &socket), recv(stdout, &socket))?;
63+
64+
Ok(())
65+
}
66+
67+
async fn send(
68+
mut stdin: impl Stream<Item = Result<Bytes, std::io::Error>> + Unpin,
69+
writer: &UdpSocket,
70+
) -> Result<(), std::io::Error> {
71+
while let Some(item) = stdin.next().await {
72+
let buf = item?;
73+
writer.send(&buf[..]).await?;
74+
}
75+
76+
Ok(())
77+
}
78+
79+
async fn recv(
80+
mut stdout: impl Sink<Bytes, Error = std::io::Error> + Unpin,
81+
reader: &UdpSocket,
82+
) -> Result<(), std::io::Error> {
83+
loop {
84+
let mut buf = vec![0; 1024];
85+
let n = reader.recv(&mut buf[..]).await?;
86+
87+
if n > 0 {
88+
stdout.send(Bytes::from(buf)).await?;
89+
}
90+
}
91+
}

examples/connect.rs

Lines changed: 0 additions & 147 deletions
This file was deleted.

examples/echo.rs renamed to examples/echo-tcp.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,13 @@
99
//!
1010
//! To see this server in action, you can run this in one terminal:
1111
//!
12-
//! cargo run --example echo
12+
//! cargo run --example echo-tcp
1313
//!
1414
//! and in another terminal you can run:
1515
//!
16-
//! cargo run --example connect 127.0.0.1:8080
16+
//! cargo run --example connect-tcp 127.0.0.1:8080
1717
//!
18-
//! Each line you type in to the `connect` terminal should be echo'd back to
18+
//! Each line you type in to the `connect-tcp` terminal should be echo'd back to
1919
//! you! If you open up multiple terminals running the `connect` example you
2020
//! should be able to see them all make progress simultaneously.
2121

examples/echo-udp.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@
66
//!
77
//! and in another terminal you can run:
88
//!
9-
//! cargo run --example connect -- --udp 127.0.0.1:8080
9+
//! cargo run --example connect-udp 127.0.0.1:8080
1010
//!
11-
//! Each line you type in to the `nc` terminal should be echo'd back to you!
11+
//! Each line you type in to the `connect-udp` terminal should be echo'd back to you!
1212
1313
#![warn(rust_2018_idioms)]
1414

examples/print_each_packet.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@
1313
//!
1414
//! and in another terminal you can run:
1515
//!
16-
//! cargo run --example connect 127.0.0.1:8080
16+
//! cargo run --example connect-tcp 127.0.0.1:8080
1717
//!
18-
//! Each line you type in to the `connect` terminal should be written to terminal!
18+
//! Each line you type in to the `connect-tcp` terminal should be written to terminal!
1919
//!
2020
//! Minimal js example:
2121
//!

0 commit comments

Comments
 (0)