Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Code formating and refine #4

Open
wants to merge 20 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,17 @@ readme = "README.md"

[dependencies]
chrono = "0.4"
clap = "~2.33.3"
core_affinity = "0.5"
ctrlc = "3.1"
env_logger = "0.8"
log = {version = "0.4", features = ["std"]}
clap = { version = "4.4", features = ["derive", "wrap_help"] }
core_affinity = "0.8"
ctrlc2 = "3.5"
env_logger = "0.10"
log = { version = "0.4", features = ["std"] }
mio = "0.6"
nix = "0.20"
serde = {version = "1.0", features = ["derive"]}
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
simple-error = "0.2"
uuid = {version = "0.8", features = ["v4"]}
simple-error = "0.3"
uuid = { version = "1.6", features = ["v4"] }

#configuration for cargo-deb
#install with "cargo install cargo-deb"
Expand Down
154 changes: 154 additions & 0 deletions src/args.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
/// rperf, validates network throughput capacity and reliability,
/// https://github.com/opensource-3d-p/rperf
#[derive(clap::Parser, Debug, Clone)]
#[command(author, version, about, long_about = None)]
pub struct Args {
/// the port used for client-server interactions
#[arg(short, long, value_name = "number", default_value_t = 5199)]
pub port: u16,

/// specify logical CPUs, delimited by commas, across which to round-robin affinity;
/// not supported on all systems
#[arg(short = 'A', long, value_name = "numbers", default_value = "")]
pub affinity: String,

/// bind to the interface associated with the address <host>
#[arg(
short = 'B',
long,
conflicts_with = "client",
default_value_if("version6", "true", Some("::")),
default_value = "0.0.0.0",
value_name = "host"
)]
pub bind: std::net::IpAddr,

/// emit debug-level logging on stderr; default is info and above
#[arg(short, long)]
pub debug: bool,

/// run in server mode
#[arg(short, long, conflicts_with = "client")]
pub server: bool,

/// enable IPv6 on the server (on most hosts, this will allow both IPv4 and IPv6,
/// but it might limit to just IPv6 on some)
#[arg(short = '6', long)]
pub version6: bool,

/// limit the number of concurrent clients that can be processed by a server;
/// any over this count will be immediately disconnected
#[arg(long, value_name = "number", default_value = "0")]
pub client_limit: usize,

/// run in client mode; value is the server's address
#[arg(short, long, value_name = "host", conflicts_with = "server")]
pub client: Option<std::net::IpAddr>,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I get what you're going for here, but this breaks compatibility with hostname-based resolution.

Not everything will, or should, be specified as an IP address.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A string?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, specifically a hostname as defined in https://datatracker.ietf.org/doc/html/rfc952 or an IP address, v4 or v6, per https://datatracker.ietf.org/doc/html/rfc1123#page-13, but I'm perfectly content for it to just be a string and for the user to figure it out if they enter something nonsensical, rather than having the tool try to hold their hand on something basic like this.


/// run in reverse-mode (server sends, client receives)
#[arg(short = 'R', long)]
pub reverse: bool,

/// the format in which to deplay information (json, megabit/sec, megabyte/sec)
#[arg(
short,
long,
value_enum,
value_name = "format",
default_value = "megabit"
)]
pub format: Format,

/// use UDP rather than TCP
#[arg(short, long)]
pub udp: bool,

/// target bandwidth in bytes/sec; this value is applied to each stream,
/// with a default target of 1 megabit/second for all protocols (note: megabit, not mebibit);
/// the suffixes kKmMgG can also be used for xbit and xbyte, respectively
#[arg(short, long, default_value = "125000", value_name = "bytes/sec")]
pub bandwidth: String,

/// the time in seconds for which to transmit
#[arg(short, long, default_value = "10.0", value_name = "seconds")]
pub time: f64,

/// the interval at which to send batches of data, in seconds, between [0.0 and 1.0);
/// this is used to evenly spread packets out over time
#[arg(long, default_value = "0.05", value_name = "seconds")]
pub send_interval: f64,

/// length of the buffer to exchange; for TCP, this defaults to 32 kibibytes; for UDP, it's 1024 bytes
#[arg(
short,
long,
default_value = "32768",
default_value_if("udp", "true", Some("1024")),
value_name = "bytes"
)]
pub length: usize,

/// send buffer, in bytes (only supported on some platforms;
/// if set too small, a 'resource unavailable' error may occur;
/// affects TCP window-size)
#[arg(long, default_value = "0", value_name = "bytes")]
pub send_buffer: usize,

/// receive buffer, in bytes (only supported on some platforms;
/// if set too small, a 'resource unavailable' error may occur; affects TCP window-size)
#[arg(long, default_value = "0", value_name = "bytes")]
pub receive_buffer: usize,

/// the number of parallel data-streams to use
#[arg(short = 'P', long, value_name = "number", default_value = "1")]
pub parallel: usize,

/// omit a number of seconds from the start of calculations,
/// primarily to avoid including TCP ramp-up in averages;
/// using this option may result in disagreement between bytes sent and received,
/// since data can be in-flight across time-boundaries
#[arg(short, long, default_value = "0", value_name = "seconds")]
Copy link
Member

@flan flan Dec 22, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

short = 'O' is required here.

The unit-tests, and existing real-world code, are broken without it, so this seems like a (minor) refactoring regression.

pub omit: usize,

/// use no-delay mode for TCP tests, disabling Nagle's Algorithm
#[arg(short = 'N', long)]
pub no_delay: bool,

/// an optional pool of IPv4 TCP ports over which data will be accepted;
/// if omitted, any OS-assignable port is used; format: 1-10,19,21
#[arg(long, value_name = "ports", default_value = "")]
pub tcp_port_pool: String,

/// an optional pool of IPv6 TCP ports over which data will be accepted;
/// if omitted, any OS-assignable port is used; format: 1-10,19,21
#[arg(long, value_name = "ports", default_value = "")]
pub tcp6_port_pool: String,

/// an optional pool of IPv4 UDP ports over which data will be accepted;
/// if omitted, any OS-assignable port is used; format: 1-10,19,21
#[arg(long, value_name = "ports", default_value = "")]
pub udp_port_pool: String,

/// an optional pool of IPv6 UDP ports over which data will be accepted;
/// if omitted, any OS-assignable port is used; format: 1-10,19,21
#[arg(long, value_name = "ports", default_value = "")]
pub udp6_port_pool: String,
}

#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, clap::ValueEnum, Default)]
pub enum Format {
#[default]
Json,
Megabit,
Megabyte,
}

impl std::fmt::Display for Format {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Format::Json => write!(f, "json"),
Format::Megabit => write!(f, "megabit/sec"),
Format::Megabyte => write!(f, "megabyte/sec"),
}
}
}
45 changes: 18 additions & 27 deletions src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,9 @@ use std::sync::{Arc, Mutex};
use std::thread;
use std::time::{Duration, SystemTime, UNIX_EPOCH};

use clap::ArgMatches;

use mio::net::TcpStream;

use crate::args;
use crate::protocol::communication::{receive, send, KEEPALIVE_DURATION};

use crate::protocol::messaging::{
Expand Down Expand Up @@ -115,56 +114,48 @@ fn prepare_test_results(is_udp: bool, stream_count: u8) -> Mutex<Box<dyn TestRes
}
}

pub fn execute(args: ArgMatches) -> BoxResult<()> {
pub fn execute(args: &args::Args) -> BoxResult<()> {
let mut complete = false;

//config-parsing and pre-connection setup
let mut tcp_port_pool = tcp::receiver::TcpPortPool::new(
args.value_of("tcp_port_pool").unwrap().to_string(),
args.value_of("tcp6_port_pool").unwrap().to_string(),
args.tcp_port_pool.to_string(),
args.tcp6_port_pool.to_string(),
);
let mut udp_port_pool = udp::receiver::UdpPortPool::new(
args.value_of("udp_port_pool").unwrap().to_string(),
args.value_of("udp6_port_pool").unwrap().to_string(),
args.udp_port_pool.to_string(),
args.udp6_port_pool.to_string(),
);

let cpu_affinity_manager = Arc::new(Mutex::new(
crate::utils::cpu_affinity::CpuAffinityManager::new(args.value_of("affinity").unwrap())?,
crate::utils::cpu_affinity::CpuAffinityManager::new(&args.affinity)?,
));

let display_json: bool;
let display_bit: bool;
match args.value_of("format").unwrap() {
"json" => {
match args.format {
args::Format::Json => {
display_json = true;
display_bit = false;
}
"megabit" => {
args::Format::Megabit => {
display_json = false;
display_bit = true;
}
"megabyte" => {
args::Format::Megabyte => {
display_json = false;
display_bit = false;
}
_ => {
log::error!("unsupported display-mode; defaulting to JSON");
display_json = true;
display_bit = false;
}
}

let is_udp = args.is_present("udp");
let is_udp = args.udp;

let test_id = uuid::Uuid::new_v4();
let mut upload_config = prepare_upload_configuration(&args, test_id.as_bytes())?;
let mut download_config = prepare_download_configuration(&args, test_id.as_bytes())?;
let mut upload_config = prepare_upload_configuration(args, test_id.as_bytes())?;
let mut download_config = prepare_download_configuration(args, test_id.as_bytes())?;

//connect to the server
let mut stream = connect_to_server(
args.value_of("client").unwrap(),
&(args.value_of("port").unwrap().parse()?),
)?;
let mut stream = connect_to_server(&args.client.unwrap().to_string(), &args.port)?;
let server_addr = stream.peer_addr()?;

//scaffolding to track and relay the streams and stream-results associated with this test
Expand Down Expand Up @@ -224,7 +215,7 @@ pub fn execute(args: ArgMatches) -> BoxResult<()> {
};

//depending on whether this is a forward- or reverse-test, the order of configuring test-streams will differ
if args.is_present("reverse") {
if args.reverse {
log::debug!("running in reverse-mode: server will be uploading data");

//when we're receiving data, we're also responsible for letting the server know where to send it
Expand Down Expand Up @@ -559,7 +550,7 @@ pub fn execute(args: ArgMatches) -> BoxResult<()> {
}

log::debug!("displaying test results");
let omit_seconds: usize = args.value_of("omit").unwrap().parse()?;
let omit_seconds: usize = args.omit;
{
let tr = test_results.lock().unwrap();
if display_json {
Expand All @@ -576,7 +567,7 @@ pub fn execute(args: ArgMatches) -> BoxResult<()> {
IpAddr::V4(_) => 4,
IpAddr::V6(_) => 6,
},
"reverse": args.is_present("reverse"),
"reverse": args.reverse,
})
)
);
Expand Down
Loading