//! Runtime-free blocking client example. //! //! Opens a session against a local IRTT-compatible server, sends a few //! probes, and prints the events observed. Uses only [`Client`], which has no //! Tokio dependency and works in a default (`--no-default-features`) build. //! //! Run a server first, then run this example: //! //! ```text //! cargo run -p irtt-rs --bin irtt-server --features server //! cargo run -p irtt-client --example blocking_client //! ``` //! //! Without a reachable server this exits quickly with an open-timeout error //! instead of hanging, because the example shortens `open.timeouts` for a //! fast demonstration; a real caller would normally keep the default retry //! schedule. use std::time::{Duration, Instant}; use irtt_client::{Client, ClientConfig, ClientEvent, OpenOutcome, OpenPolicy, SessionRequest}; const RUN_DURATION: Duration = Duration::from_secs(2); fn main() { let config = ClientConfig { open: OpenPolicy { // A single short attempt keeps this example fast when no server is // reachable; production callers should generally keep the default. timeouts: vec![Duration::from_millis(300)], ..Default::default() }, request: SessionRequest { duration: Some(RUN_DURATION), interval: Duration::from_millis(200), ..Default::default() }, ..Default::default() }; let mut client = match Client::connect("127.0.0.1:2112", config) { Ok(client) => client, Err(err) => { eprintln!("failed to prepare client socket: {err}"); return; } }; if let Err(err) = client.set_recv_timeout(Some(Duration::from_millis(100))) { eprintln!("failed to configure receive timeout: {err}"); return; } let negotiated = match client.open() { Ok(OpenOutcome::Started { event, negotiated, .. }) => { println!("session opened: {event:?}"); negotiated } Ok(OpenOutcome::NoTestCompleted { event, .. }) => { println!("{event:?}"); return; } Err(err) => { eprintln!("open failed (is a server running at 127.0.0.1:2112?): {err}"); return; } }; // This loop owns cadence and duration. The transport sends whenever called. let interval = Duration::from_nanos(negotiated.params.interval_ns as u64); let start = Instant::now(); let end = start + Duration::from_nanos(negotiated.params.duration_ns as u64); let deadline = end + client.probe_timeout(); let mut next_send = start; while Instant::now() < deadline && !client.is_peer_closed() { let now = Instant::now(); if now >= end && !client.has_pending_probes() { break; } if now < end && now >= next_send { match client.send_probe() { Ok(events) => { events.iter().for_each(print_event); while next_send <= Instant::now() { next_send += interval; } } Err(err) => { eprintln!("send failed: {err}"); break; } } } match client.recv_once() { Ok(events) => events.iter().for_each(print_event), Err(err) => { eprintln!("receive failed: {err}"); break; } } match client.poll_timeouts() { Ok(events) => events.iter().for_each(print_event), Err(err) => { eprintln!("timeout polling failed: {err}"); break; } } } if client.is_peer_closed() { return; } match client.close() { Ok(events) => events.iter().for_each(print_event), Err(err) => eprintln!("close failed: {err}"), } } fn print_event(event: &ClientEvent) { match event { ClientEvent::EchoReply { seq, rtt, .. } => { println!("seq {seq}: rtt {:?}", rtt.effective); } ClientEvent::EchoLoss { seq, .. } => println!("seq {seq}: lost"), other => println!("{other:?}"), } }