1//! A UDP client that just sends everything it gets via `stdio` in a single datagram, and then
2//! waits for a reply.
3//!
4//! For the reasons of simplicity data from `stdio` is read until `EOF` in a blocking manner.
5//!
6//! You can test this out by running an echo server:
7//!
8//! ```
9//! $ cargo run --example echo-udp -- 127.0.0.1:8080
10//! ```
11//!
12//! and running the client in another terminal:
13//!
14//! ```
15//! $ cargo run --example udp-client
16//! ```
17//!
18//! You can optionally provide any custom endpoint address for the client:
19//!
20//! ```
21//! $ cargo run --example udp-client -- 127.0.0.1:8080
22//! ```
23//!
24//! Don't forget to pass `EOF` to the standard input of the client!
25//!
26//! Please mind that since the UDP protocol doesn't have any capabilities to detect a broken
27//! connection the server needs to be run first, otherwise the client will block forever.
28
29#![warn(rust_2018_idioms)]
30
31use std::env;
32use std::error::Error;
33use std::io::{stdin, Read};
34use std::net::SocketAddr;
35use tokio::net::UdpSocket;
36
37fn get_stdin_data() -> Result<Vec<u8>, Box<dyn std::error::Error>> {
38 let mut buf = Vec::new();
39 stdin().read_to_end(&mut buf)?;
40 Ok(buf)
41}
42
43#[tokio::main]
44async fn main() -> Result<(), Box<dyn Error>> {
45 let remote_addr: SocketAddr = env::args()
46 .nth(1)
47 .unwrap_or_else(|| "127.0.0.1:8080".into())
48 .parse()?;
49
50 // We use port 0 to let the operating system allocate an available port for us.
51 let local_addr: SocketAddr = if remote_addr.is_ipv4() {
52 "0.0.0.0:0"
53 } else {
54 "[::]:0"
55 }
56 .parse()?;
57
58 let socket = UdpSocket::bind(local_addr).await?;
59 const MAX_DATAGRAM_SIZE: usize = 65_507;
60 socket.connect(&remote_addr).await?;
61 let data = get_stdin_data()?;
62 socket.send(&data).await?;
63 let mut data = vec![0u8; MAX_DATAGRAM_SIZE];
64 let len = socket.recv(&mut data).await?;
65 println!(
66 "Received {} bytes:\n{}",
67 len,
68 String::from_utf8_lossy(&data[..len])
69 );
70
71 Ok(())
72}
73