tokio_client/
tokio_client.rs1use std::time::{Duration, Instant};
20
21use irtt_client::{
22 AsyncClient, ClientConfig, ClientEvent, OpenOutcome, OpenPolicy, SessionRequest,
23};
24
25const TIMEOUT_POLL_INTERVAL: Duration = Duration::from_millis(50);
33
34fn main() {
35 let runtime = tokio::runtime::Builder::new_current_thread()
36 .enable_io()
37 .enable_time()
38 .build()
39 .expect("failed to build Tokio runtime");
40
41 runtime.block_on(run());
42}
43
44async fn run() {
45 let config = ClientConfig {
46 open: OpenPolicy {
47 timeouts: vec![Duration::from_millis(300)],
50 ..Default::default()
51 },
52 request: SessionRequest {
53 duration: Some(Duration::from_secs(2)),
54 interval: Duration::from_millis(200),
55 ..Default::default()
56 },
57 ..Default::default()
58 };
59
60 let mut client = match AsyncClient::connect("127.0.0.1:2112", config).await {
61 Ok(client) => client,
62 Err(err) => {
63 eprintln!("failed to prepare client socket: {err}");
64 return;
65 }
66 };
67
68 let negotiated = match client.open().await {
69 Ok(OpenOutcome::Started(started)) => {
70 println!("session opened: {started:?}");
71 started.negotiation
72 }
73 Ok(OpenOutcome::NoTestCompleted(completed)) => {
74 println!("{completed:?}");
75 return;
76 }
77 Err(err) => {
78 eprintln!("open failed (is a server running at 127.0.0.1:2112?): {err}");
79 return;
80 }
81 };
82
83 let interval = negotiated.accepted.interval;
85 let start = Instant::now();
86 let end = start
87 + negotiated
88 .accepted
89 .duration
90 .expect("example requests a finite run");
91 let deadline = end + client.probe_timeout();
92 let mut next_send = start;
93 while Instant::now() < deadline && !client.is_peer_closed() {
94 if Instant::now() >= end && !client.has_pending_probes() {
95 break;
96 }
97 let wake_at = (next_send < end)
101 .then_some(next_send)
102 .into_iter()
103 .chain(client.next_probe_timeout_deadline())
104 .chain(std::iter::once(Instant::now() + TIMEOUT_POLL_INTERVAL))
105 .min()
106 .unwrap();
107
108 let recv_result = tokio::select! {
109 events = client.recv() => Some(events),
110 () = tokio::time::sleep_until(tokio::time::Instant::from_std(wake_at)) => None,
111 };
112 if let Some(result) = recv_result {
113 match result {
114 Ok(events) => events.iter().for_each(print_event),
115 Err(err) => {
116 eprintln!("receive failed: {err}");
117 break;
118 }
119 }
120 }
121
122 if Instant::now() < end && Instant::now() >= next_send {
123 match client.send_probe().await {
124 Ok(receipt) => {
125 print_event(&receipt.into());
126 while next_send <= Instant::now() {
127 next_send += interval;
128 }
129 }
130 Err(err) => {
131 if let irtt_client::SendProbeError::AfterCommit { receipt, .. } = &err {
132 print_event(&(*receipt).into());
133 }
134 eprintln!("send failed: {err}");
135 break;
136 }
137 }
138 }
139 match client.poll_timeouts() {
140 Ok(events) => events.iter().for_each(print_event),
141 Err(err) => {
142 eprintln!("timeout polling failed: {err}");
143 break;
144 }
145 }
146 }
147
148 if client.is_peer_closed() {
149 return;
150 }
151 match client.close().await {
152 Ok(events) => events.iter().for_each(print_event),
153 Err(err) => eprintln!("close failed: {err}"),
154 }
155}
156
157fn print_event(event: &ClientEvent) {
158 match event {
159 ClientEvent::EchoReply { seq, rtt, .. } => {
160 println!("seq {seq}: rtt {:?}", rtt.effective);
161 }
162 ClientEvent::EchoLoss { seq, .. } => println!("seq {seq}: lost"),
163 other => println!("{other:?}"),
164 }
165}