diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..cf8cb73 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "rperf" +version = "0.1.0" +edition = "2024" + +[dependencies] +clap = { version = "4.5.40", features = ["derive"] } diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..4ee4216 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,58 @@ +mod send; +mod receive; + +use std::io; +use clap::{Parser, Subcommand}; + +#[derive(Parser)] +#[command(name = "rperf")] +#[command(about = "A simple CLI for sending and receiving UDP messages", long_about = None)] +struct Cli { + #[command(subcommand)] + command: Commands, +} + +#[derive(Subcommand)] +enum Commands { + /// Send a message to a specified host and port + Send { + /// The target IP address and port: host:port + #[arg(long)] + host: String, + + /// The message to send + #[arg(short, long)] + message: String, + + /// Number of times to send + #[arg(short, long)] + iters: u32, + }, + /// Receive a message from a specified host and port + Receive { + /// The src IP address and port: host:port + #[arg(long)] + host: String, + + /// Number of times to receive + #[arg(short, long)] + iters: u32, + }, +} + +fn main() -> io::Result<()> { + let cli = Cli::parse(); + + match cli.command { + Commands::Send { host, message, iters } => { + println!("Sending message '{}' to {}", message, host); + send::send(&host, &message, iters)? + } + Commands::Receive { host, iters } => { + println!("Receiving messages from {}", host); + receive::receive(&host, iters)? + } + } + + Ok(()) +} diff --git a/src/receive.rs b/src/receive.rs new file mode 100644 index 0000000..bfba1dc --- /dev/null +++ b/src/receive.rs @@ -0,0 +1,34 @@ +use std::net::UdpSocket; +use std::io; +use std::time::Instant; + +pub fn receive(host: &str, iters: u32) -> io::Result<()> { + let socket = UdpSocket::bind(host).expect("Failed to bind socket"); + let packet_size = 1080; + + let mut buf = vec![0u8; packet_size]; + let mut total_size: usize = 0; + let mut start: Option = None; + + for i in 0..iters { + match socket.recv_from(&mut buf) { + Ok((size, _)) => { + if start.is_none() { + start = Some(Instant::now()); + } + total_size += size; + if i % 10000 == 0 { + let elapsed = start.unwrap().elapsed().as_secs_f64(); + let mbps = (total_size * 8) as f64 / 1e6 / elapsed; + let pps = 10000.0 / elapsed; + println!("{}: Received {} bytes in {} seconds ({} mbps, {} pps)", i, total_size, elapsed, mbps, pps); + total_size = 0; + start = None; + } + }, + Err(e) => eprintln!("Failed to receive message: {}", e), + } + } + + Ok(()) +} diff --git a/src/send.rs b/src/send.rs new file mode 100644 index 0000000..2ab9839 --- /dev/null +++ b/src/send.rs @@ -0,0 +1,21 @@ +use std::net::UdpSocket; +use std::io; + +pub fn send(host: &str, message: &str, iters: u32) -> io::Result<()> { + let socket = UdpSocket::bind("0.0.0.0:0").expect("Failed to bind socket"); + let packet_size = 1080; + + let message = String::from(message); + let padding = "\0".repeat(packet_size - message.len()); + let buf = message + &padding; + + + for _ in 0..iters { + match socket.send_to(buf.as_bytes(), host) { + Ok(_) => (), + Err(e) => eprintln!("Failed to send message: {}", e), + } + } + + Ok(()) +}