Skip to main content

blocking_client/
blocking_client.rs

1//! Runtime-free blocking client example.
2//!
3//! Opens a session against a local IRTT-compatible server, sends a few
4//! probes, and prints the events observed. Uses only [`Client`], which has no
5//! Tokio dependency and works in a default (`--no-default-features`) build.
6//!
7//! Run a server first, then run this example:
8//!
9//! ```text
10//! cargo run -p irtt-rs --bin irtt-server --features server
11//! cargo run -p irtt-client --example blocking_client
12//! ```
13//!
14//! Without a reachable server this exits quickly with an open-timeout error
15//! instead of hanging, because the example shortens `open.timeouts` for a
16//! fast demonstration; a real caller would normally keep the default retry
17//! schedule.
18
19use std::time::{Duration, Instant};
20
21use irtt_client::{Client, ClientConfig, ClientEvent, OpenOutcome, OpenPolicy, SessionRequest};
22
23const RUN_DURATION: Duration = Duration::from_secs(2);
24
25fn main() {
26    let config = ClientConfig {
27        open: OpenPolicy {
28            // A single short attempt keeps this example fast when no server is
29            // reachable; production callers should generally keep the default.
30            timeouts: vec![Duration::from_millis(300)],
31            ..Default::default()
32        },
33        request: SessionRequest {
34            duration: Some(RUN_DURATION),
35            interval: Duration::from_millis(200),
36            ..Default::default()
37        },
38        ..Default::default()
39    };
40
41    let mut client = match Client::connect("127.0.0.1:2112", config) {
42        Ok(client) => client,
43        Err(err) => {
44            eprintln!("failed to prepare client socket: {err}");
45            return;
46        }
47    };
48
49    if let Err(err) = client.set_recv_timeout(Some(Duration::from_millis(100))) {
50        eprintln!("failed to configure receive timeout: {err}");
51        return;
52    }
53
54    let negotiated = match client.open() {
55        Ok(OpenOutcome::Started(started)) => {
56            println!("session opened: {started:?}");
57            started.negotiation
58        }
59        Ok(OpenOutcome::NoTestCompleted(completed)) => {
60            println!("{completed:?}");
61            return;
62        }
63        Err(err) => {
64            eprintln!("open failed (is a server running at 127.0.0.1:2112?): {err}");
65            return;
66        }
67    };
68
69    // This loop owns cadence and duration. The transport sends whenever called.
70    let interval = negotiated.accepted.interval;
71    let start = Instant::now();
72    let end = start
73        + negotiated
74            .accepted
75            .duration
76            .expect("example requests a finite run");
77    let deadline = end + client.probe_timeout();
78    let mut next_send = start;
79    while Instant::now() < deadline && !client.is_peer_closed() {
80        let now = Instant::now();
81        if now >= end && !client.has_pending_probes() {
82            break;
83        }
84        if now < end && now >= next_send {
85            match client.send_probe() {
86                Ok(receipt) => {
87                    print_event(&receipt.into());
88                    while next_send <= Instant::now() {
89                        next_send += interval;
90                    }
91                }
92                Err(err) => {
93                    if let irtt_client::SendProbeError::AfterCommit { receipt, .. } = &err {
94                        print_event(&(*receipt).into());
95                    }
96                    eprintln!("send failed: {err}");
97                    break;
98                }
99            }
100        }
101        match client.recv_once() {
102            Ok(events) => events.iter().for_each(print_event),
103            Err(err) => {
104                eprintln!("receive failed: {err}");
105                break;
106            }
107        }
108        match client.poll_timeouts() {
109            Ok(events) => events.iter().for_each(print_event),
110            Err(err) => {
111                eprintln!("timeout polling failed: {err}");
112                break;
113            }
114        }
115    }
116
117    if client.is_peer_closed() {
118        return;
119    }
120    match client.close() {
121        Ok(events) => events.iter().for_each(print_event),
122        Err(err) => eprintln!("close failed: {err}"),
123    }
124}
125
126fn print_event(event: &ClientEvent) {
127    match event {
128        ClientEvent::EchoReply { seq, rtt, .. } => {
129            println!("seq {seq}: rtt {:?}", rtt.effective);
130        }
131        ClientEvent::EchoLoss { seq, .. } => println!("seq {seq}: lost"),
132        other => println!("{other:?}"),
133    }
134}