Rust CLI utility for measuring link latency over TCP and UDP. It provides a symmetrical server/client pair with echo semantics, high-resolution RTT tracking, jitter/loss stats, concurrent TCP+UDP testing, and colorful tabular reports designed for real-world network troubleshooting.
- Dual-protocol server – one process listens on both TCP & UDP (default port
9000), sharing the same Ctrl+C shutdown signal. - Client echo tests – TCP payloads are length-prefixed, UDP payloads contain sequence & magic headers for validation.
- Accurate RTT & jitter – timestamps captured in milliseconds (f64) with jitter computed over deltas.
- Loss & timeout tracking – number of sent/received packets, loss %, and timed-out exchanges.
- Concurrent TCP+UDP measurements –
--proto both(default) runs both protocols in parallel and prints a unified colored table. - Non-blocking server logging – application logs are streamed through an async logger thread to avoid affecting RTT.
- Progress display – live progress line (
TCP x/y | UDP x/y) while concurrent tests run.
# Debug build
cargo build
# Release build
cargo build --releaseBinaries are emitted into target/debug/ and target/release/ respectively.
LinkLatencyTester.exe server [--proto both|tcp|udp] [--bind 0.0.0.0] \
[--tcp-port 9000] [--udp-port 9000] [--echo true]--proto both(default) starts TCP & UDP listeners simultaneously.--echo true/falsecontrols whether payloads are echoed back. Echo drives RTT measurement.
Example:
LinkLatencyTester.exe server --proto both --bind 0.0.0.0 --tcp-port 9000 --udp-port 9000LinkLatencyTester.exe client --host <server_ip> \
[--proto both|tcp|udp] [--tcp-port 9000] [--udp-port 9000] \
[--size 16] [--count 20] [--interval-ms 200] [--timeout-ms 1000]--proto both(default) runs TCP+UDP concurrently and prints one summary table.- Single-protocol modes retain verbose per-packet logging (send/recv timestamps, RTT, len, errors).
- Progress line (e.g.
TCP 7/20 | UDP 5/20) refreshes until both protocols finish. --plainforces ASCII borders + no colors/icons (for limited terminals) while keeping the default styling otherwise.
Example:
LinkLatencyTester.exe client --host 203.0.113.10 --proto both --count 30 --size 32After the latency table, the client can optionally print a hop-by-hop list using the built-in tracert integration (default: enabled).
--trace=true|falseturns hop printing on or off.--trace-max-hops,--trace-wait-ms,--trace-send-rate-ms, and--trace-timeout-smap directly to the tracer settings.- Output respects
--plain, so hop rows stay ASCII-friendly when needed.
Windows requirements:
tracertuses raw sockets. Run the CLI from an elevated PowerShell/Command Prompt and allow ICMP "Time-to-live Exceeded" / "Destination Unreachable" in Windows Firewall. You can temporarily enable this with:netsh advfirewall firewall add rule name="All ICMP v4" dir=in action=allow protocol=icmpv4:any,any netsh advfirewall firewall add rule name="All ICMP v6" dir=in action=allow protocol=icmpv6:any,any
Example command that runs latency tests plus traceroute:
LinkLatencyTester.exe client --host 203.0.113.10 --proto both --count 20 --trace --trace-max-hops 30TCP 20/20 | UDP 20/20
Target: tcp=203.0.113.10:9000 udp=203.0.113.10:9000
┌───────────┬─────┬─────┬─────┬──────┬────────┬────────┬─────────┬────────┬────┐
│ PROTO ┆ SENT┆ RECV┆ LOST┆ LOSS%│ MIN(ms)│ AVG(ms)│ MAX(ms) │ JIT(ms)│ TO │
╞═══════════╪═════╪═════╪═════╪══════╪════════╪════════╪═════════╪════════╪════╡
│ TCP ┆ 20 ┆ 19 ┆ 1 ┆ 5.00 │ 63.286 │ 678.392│ 1122.932│ 126.247│ 1 │
├───────────┼─────┼─────┼─────┼──────┼────────┼────────┼─────────┼────────┼────┤
│ UDP ┆ 20 ┆ 16 ┆ 4 ┆ 20.00│ 107.075│ 114.168│ 119.912 │ 3.076 │ 4 │
└───────────┴─────┴─────┴─────┴──────┴────────┴────────┴─────────┴────────┴────┘
Colors highlight good (green), warning (yellow), or poor (red) performance thresholds.
Need compatibility mode? Append
--plainto the client command above to render the ASCII table without colors or box-drawing glyphs.
Traceroute hop list (excerpt):
Trace (tracert): 203.0.113.10 (max_hops=30 wait_ms=1500 send_rate_ms=0 timeout_s=30)
status: Done
1) 192.168.1.1 hop=1 ttl=64 rtt=2.7 ms (Δn/a) default-gateway
2) 10.10.0.2 hop=2 ttl=63 rtt=4.1 ms (Δ+1.4 ms) relay
3) 125.68.140.1 hop=3 ttl=253 rtt=9.3 ms (Δ+5.2 ms) relay
4) 182.134.236.157 hop=4 ttl=252 rtt=15.0 ms (Δ+5.7 ms) relay
5) 202.97.29.33 hop=5 ttl=250 rtt=42.1 ms (Δ+27.1 ms) relay
6) 121.189.3.105 hop=6 ttl=246 rtt=88.4 ms (Δ+46.3 ms) relay
7) 203.0.113.10 hop=7 ttl=242 rtt=95.0 ms (Δ+6.6 ms) destination
trace duration: 8.36s
| Option | Default | Description |
|---|---|---|
--proto |
both |
Choose tcp, udp, or both for dual-mode. |
--bind (server) |
0.0.0.0 |
Address to bind for TCP/UDP listeners. |
--tcp-port / --udp-port |
9000 |
Ports for each protocol. |
--echo (server) |
true |
Whether to echo payloads back. |
--host (client) |
required | Target server hostname/IP. |
--size |
16 |
Payload size in bytes (>= 8 reserved for header). |
--count |
20 |
Number of packets per protocol. |
--interval-ms |
200 |
Delay between packets. |
--timeout-ms |
1000 |
Socket read/write timeout per packet. |
- TCP payload framing (
u32length + payload) prevents sticky packets and preserves 1:1 RTT mapping. - UDP payload embeds sequence+magic to validate responses.
- Server log output uses a background thread (
mpscchannel) to avoid affecting response latency. - Ctrl+C handler flips an
AtomicBool, allowing graceful shutdown of both protocols.
Feel free to clone, tweak defaults, or extend the tool for your latency testing workflows.