Skip to content

Commit

Permalink
feat: add ICMP ping fallback to TCP mechanism (#33)
Browse files Browse the repository at this point in the history
Co-authored-by: pk5ls20 <pk5ls20@outlook.com>
  • Loading branch information
Asankilp and pk5ls20 authored Mar 3, 2025
1 parent ce8d99a commit c37231c
Showing 1 changed file with 42 additions and 4 deletions.
46 changes: 42 additions & 4 deletions mania/src/core/ping.rs
Original file line number Diff line number Diff line change
@@ -1,25 +1,40 @@
use std::io::Result;
use std::net::SocketAddr;
use std::time::Duration;

use std::time::{Duration, Instant};
use surge_ping::{Client, Config, ICMP, PingIdentifier, PingSequence};
use tokio::net::TcpStream;
use tokio::task::JoinSet;

/// Ping a list of addresses and return the latency.
pub async fn ping(addrs: Vec<SocketAddr>, ipv6: bool) -> Result<Vec<(SocketAddr, Duration)>> {
// 首先尝试ICMP ping
let icmp = Client::new(&if ipv6 {
Config::builder().kind(ICMP::V6).build()
} else {
Config::default()
})?;
});

match icmp {
Ok(icmp) => ping_with_icmp(icmp, addrs).await,
Err(e) => {
tracing::debug!("ICMP ping failed: {}, fallback to TCP ping", e);
ping_with_tcp(addrs).await
}
}
}

async fn ping_with_icmp(
icmp: Client,
addrs: Vec<SocketAddr>,
) -> Result<Vec<(SocketAddr, Duration)>> {
let mut pingers = Vec::with_capacity(addrs.len());
for (i, addr) in addrs.iter().enumerate() {
let pinger = icmp.pinger(addr.ip(), PingIdentifier(i as u16)).await;
pingers.push(pinger);
}

let mut results = Vec::with_capacity(addrs.len());
let mut join_set = tokio::task::JoinSet::new();
let mut join_set = JoinSet::new();
for (mut pinger, addr) in pingers.into_iter().zip(addrs) {
join_set.spawn(async move {
let latency = pinger
Expand All @@ -35,3 +50,26 @@ pub async fn ping(addrs: Vec<SocketAddr>, ipv6: bool) -> Result<Vec<(SocketAddr,

Ok(results)
}

async fn ping_with_tcp(addrs: Vec<SocketAddr>) -> Result<Vec<(SocketAddr, Duration)>> {
let mut results = Vec::with_capacity(addrs.len());
let mut join_set = JoinSet::new();

for addr in addrs {
join_set.spawn(async move {
let start = Instant::now();
let timeout = Duration::from_secs(1);
let latency = match tokio::time::timeout(timeout, TcpStream::connect(addr)).await {
Ok(Ok(_)) => start.elapsed(),
_ => Duration::MAX,
};
(addr, latency)
});
}

while let Some(result) = join_set.join_next().await {
results.push(result?);
}

Ok(results)
}

0 comments on commit c37231c

Please sign in to comment.