Skip to content

Commit

Permalink
feat: supports concurrent pinging of n ip's under one address.
Browse files Browse the repository at this point in the history
  • Loading branch information
hanshuaikang committed Jan 4, 2025
1 parent 73f6a0c commit f379c34
Show file tree
Hide file tree
Showing 6 changed files with 56 additions and 30 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "nping"
version = "0.2.0"
version = "0.2.1"
edition = "2021"

[dependencies]
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ nping --help
- Supports visual latency display
- Real-time display of maximum, minimum, average latency, packet loss rate, and other metrics
- Support IpV4 and IpV6
- Supports concurrent pinging of n ip's under one address.

## TODO:
- Support dynamic layout display
Expand Down
1 change: 1 addition & 0 deletions README_ZH.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ nping --help
- 支持可视化延迟展示
- 实时最大最小平均延迟丢包率等指标展示
- 支持 IpV4 和 IpV6
- 支持一个地址下并发 Ping n 个 ip

## TODO:
- 支持动态布局展示
Expand Down
39 changes: 26 additions & 13 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
// 引入自定义模块
mod network;
mod ui;
mod terminal;
Expand All @@ -13,9 +12,9 @@ use crate::network::send_ping;

#[derive(Parser, Debug)]
#[command(
version = "v0.2.0",
version = "v0.2.1",
author = "hanshuaikang<https://github.com/hanshuaikang>",
about = "🏎 Nping with concurrent,chart,multiple addresses,real-time data update"
about = "🏎 Nping mean NB Ping, A Ping Tool in Rust with Real-Time Data and Visualizations"
)]
struct Args {
/// Target IP address or hostname to ping
Expand All @@ -33,6 +32,13 @@ struct Args {
#[clap(long = "force_ipv6", default_value_t = false, short = '6', help = "Force using IPv6")]
pub force_ipv6: bool,

#[arg(
short = 'm',
long,
default_value_t = 0,
help = "Specify the maximum number of target addresses, Only works on one target address"
)]
multiple: i32,
}


Expand All @@ -55,7 +61,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {

let targets: Vec<String> = args.target.into_iter().collect::<HashSet<_>>().into_iter().collect();

let res = run_app(targets, args.count, args.interval, running.clone(), args.force_ipv6).await;
let res = run_app(targets, args.count, args.interval, running.clone(), args.force_ipv6, args.multiple).await;

// if error print error message and exit
if let Err(err) = res {
Expand All @@ -71,6 +77,7 @@ async fn run_app(
interval: i32,
running: Arc<Mutex<bool>>,
force_ipv6: bool,
multiple: i32,
) -> Result<(), Box<dyn std::error::Error>> {

// init terminal
Expand All @@ -80,10 +87,23 @@ async fn run_app(
let terminal = ui::init_terminal().unwrap();
let terminal_guard = Arc::new(Mutex::new(terminal::TerminalGuard::new(terminal)));

let mut addrs = Vec::new();
// if multiple is set, get multiple IP addresses for each target
if targets.len() == 1 && multiple > 0 {
// 使用get_host_ipaddrs获取多个IP
addrs = network::get_multiple_host_ipaddr(&targets[0], force_ipv6, multiple as usize)?;
} else {
// get IP address for each target
for target in &targets {
let ip = network::get_host_ipaddr(target, force_ipv6)?;
addrs.push(ip);
}
}

// Define statistics variables
let ip_data = Arc::new(Mutex::new(targets.iter().map(|target| IpData {
let ip_data = Arc::new(Mutex::new(addrs.iter().enumerate().map(|(i, _)| IpData {
ip: String::new(),
addr: target.to_string(),
addr: if targets.len() == 1 { targets[0].clone() } else { targets[i].clone() },
rtts: VecDeque::new(),
last_attr: 0.0,
min_rtt: 0.0,
Expand All @@ -95,13 +115,6 @@ async fn run_app(

let errs = Arc::new(Mutex::new(Vec::new()));

// Resolve target addresses
let mut addrs = Vec::new();
for target in targets {
let ip = network::get_host_ipaddr(&target, force_ipv6)?;
addrs.push(ip);
}

let interval = if interval == 0 { 500 } else { interval * 1000 };
let mut tasks = Vec::new();
for (i, addr) in addrs.iter().enumerate() {
Expand Down
41 changes: 26 additions & 15 deletions src/network.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ use pinger::{ping, PingOptions, PingResult};
use crate::ip_data::IpData;

// get host ip address default to ipv4
pub(crate) fn get_host_ipaddr(host: &str, force_ipv6: bool) -> Result<String, Box<dyn Error>> {
pub(crate) fn resolve_host_ips(host: &str, force_ipv6: bool) -> Result<Vec<IpAddr>, Box<dyn Error>> {
// 获取所有IP地址
let ipaddr: Vec<_> = (host, 80)
.to_socket_addrs()
.with_context(|| format!("failed to resolve host: {}", host))?
Expand All @@ -18,21 +19,31 @@ pub(crate) fn get_host_ipaddr(host: &str, force_ipv6: bool) -> Result<String, Bo
return Err(anyhow!("Could not resolve host: {}", host).into());
}

if force_ipv6 {
let ipaddr = ipaddr
.iter()
.find(|ip| matches!(ip, IpAddr::V6(_)))
.ok_or_else(|| anyhow!("Could not resolve '{}' to ipv6", host))?;
return Ok(ipaddr.to_string());
}

let ipaddr = ipaddr
.iter()
.find(|ip| matches!(ip, IpAddr::V4(_)))
.ok_or_else(|| anyhow!("Could not resolve '{}' to ipv4", host))?;
// 根据 force_ipv6 过滤 IP 地址
let filtered_ips = if force_ipv6 {
ipaddr.into_iter()
.filter(|ip| matches!(ip, IpAddr::V6(_)))
.collect()
} else {
ipaddr.into_iter()
.filter(|ip| matches!(ip, IpAddr::V4(_)))
.collect()
};

Ok(filtered_ips)
}

pub(crate) fn get_host_ipaddr(host: &str, force_ipv6: bool) -> Result<String, Box<dyn Error>> {
let ips = resolve_host_ips(host, force_ipv6)?;
Ok(ips[0].to_string())
}

Ok(ipaddr.to_string())
pub(crate) fn get_multiple_host_ipaddr(host: &str, force_ipv6: bool, multiple: usize) -> Result<Vec<String>, Box<dyn Error>> {
let ips = resolve_host_ips(host, force_ipv6)?;
Ok(ips.into_iter()
.take(multiple)
.map(|ip| ip.to_string())
.collect())
}


Expand Down Expand Up @@ -93,7 +104,7 @@ impl PingTask {
PingResult::Pong(duration, _size) => {
// calculate rtt
let rtt = duration.as_secs_f64() * 1000.0;
let rtt_display: f64 = format!("{:.2}", rtt).parse().unwrap();
let rtt_display: f64 = format!("{:.2}", rtt).parse().unwrap();
update_stats(
self.ip_data.clone(),
self.index,
Expand Down

0 comments on commit f379c34

Please sign in to comment.