#!/usr/bin/env node /** UDP echo RTT. server: node udp-probe.mjs server [port] * client: node udp-probe.mjs client [port] [size] */ import dgram from "node:dgram"; const mode = process.argv[2] || "server"; const port = Number(process.argv[mode === "server" ? 3 : 5] || 9999); if (mode === "server") { const s = dgram.createSocket("udp4"); s.on("message", (msg, rinfo) => s.send(msg, rinfo.port, rinfo.address)); s.bind(port, "0.0.0.0", () => console.log(JSON.stringify({ mode: "udp-server", port }))); } else { const host = process.argv[3]; const count = Number(process.argv[4] || 1000); const size = Number(process.argv[6] || 128); const sock = dgram.createSocket("udp4"); const payload = Buffer.alloc(size, 7); const samples = []; let i = 0; const sendOne = () => { const t0 = process.hrtime.bigint(); const once = (msg) => { sock.off("message", once); samples.push(Number(process.hrtime.bigint() - t0) / 1000); i += 1; if (i >= count) { samples.sort((a, b) => a - b); const us = (n) => `${(n / 1000).toFixed(3)}ms`; const pct = (p) => samples[Math.min(samples.length - 1, Math.floor((p / 100) * samples.length))]; const sum = samples.reduce((a, b) => a + b, 0); console.log( JSON.stringify({ mode: "udp-ping", count: samples.length, size, host, min: us(samples[0]), avg: us(sum / samples.length), p50: us(pct(50)), p99: us(pct(99)), max: us(samples[samples.length - 1]), p50_us: Math.round(pct(50)), p99_us: Math.round(pct(99)), }), ); sock.close(); } else sendOne(); }; sock.on("message", once); sock.send(payload, port, host); }; sendOne(); }