From 12f7eed8a542da75c56b4ba1ed7f3deb0176ce91 Mon Sep 17 00:00:00 2001 From: lbl <1791778603@qq.com> Date: Fri, 27 Feb 2026 21:33:47 +0800 Subject: [PATCH] =?UTF-8?q?=E8=AE=B0=E5=BD=95=E5=85=B7=E4=BD=93=E8=B7=AF?= =?UTF-8?q?=E5=BE=84=E7=9A=84=E4=B8=A2=E5=8C=85=E7=8E=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- vnt-core/src/api/mod.rs | 16 ++- vnt-core/src/context/mod.rs | 102 +++++++++++++++--- vnt-core/src/tunnel_core/p2p/inbound.rs | 10 +- vnt-core/src/tunnel_core/p2p/route_table.rs | 15 +++ .../src/tunnel_core/p2p/transport/task.rs | 9 +- vnt-web/src/service_http.rs | 3 + 6 files changed, 129 insertions(+), 26 deletions(-) diff --git a/vnt-core/src/api/mod.rs b/vnt-core/src/api/mod.rs index a551b9f..5fd93e5 100644 --- a/vnt-core/src/api/mod.rs +++ b/vnt-core/src/api/mod.rs @@ -67,14 +67,26 @@ impl VntApi { pub fn peer_nat_info(&self, ip: &Ipv4Addr) -> Option { self.app_state.get_peer_info(ip).and_then(|v| v.nat_info) } + /// 获取指定 IP 的聚合丢包信息(所有路由合并) pub fn packet_loss_info(&self, ip: &Ipv4Addr) -> Option { - self.app_state.packet_loss_stats.get_loss_info(ip) + self.app_state + .packet_loss_stats + .get_aggregated_loss_info(ip) + } + /// 获取指定 IP 的所有路由的丢包信息 + pub fn packet_loss_info_by_routes(&self, ip: &Ipv4Addr) -> Vec { + self.app_state.packet_loss_stats.get_loss_info_by_ip(ip) } pub fn all_packet_loss_info(&self) -> Vec { self.app_state.packet_loss_stats.get_all_loss_info() } pub fn reset_packet_loss(&self, ip: &Ipv4Addr) { - self.app_state.packet_loss_stats.reset(ip) + // 重置该 IP 的所有路由统计 + for info in self.app_state.packet_loss_stats.get_loss_info_by_ip(ip) { + if let Some(route_key) = info.route_key { + self.app_state.packet_loss_stats.reset(ip, &route_key); + } + } } pub fn reset_all_packet_loss(&self) { self.app_state.packet_loss_stats.reset_all() diff --git a/vnt-core/src/context/mod.rs b/vnt-core/src/context/mod.rs index a65cc47..3f2cf3d 100644 --- a/vnt-core/src/context/mod.rs +++ b/vnt-core/src/context/mod.rs @@ -8,6 +8,7 @@ use crate::tunnel_core::server::transport::config::ProtocolAddress; use ipnet::Ipv4Net; use parking_lot::{Mutex, RwLock}; use rust_p2p_core::nat::NatInfo; +use rust_p2p_core::route::RouteKey; use serde::{Deserialize, Serialize}; use std::collections::HashMap; use std::net::Ipv4Addr; @@ -108,45 +109,55 @@ impl TrafficStats { #[derive(Clone, Default)] pub struct PacketLossStats { - inner: Arc>>>>, + inner: Arc>>>>, } #[derive(Clone, Debug, Serialize, Deserialize)] pub struct PacketLossInfo { pub ip: Ipv4Addr, + #[serde(skip)] + pub route_key: Option, pub sent: u64, pub received: u64, pub loss_rate: f64, } impl PacketLossStats { - fn get_or_create(&self, ip: Ipv4Addr) -> Arc> { + fn get_or_create(&self, ip: Ipv4Addr, route_key: RouteKey) -> Arc> { { let read = self.inner.read(); - if let Some(stats) = read.get(&ip) { + if let Some(stats) = read.get(&(ip, route_key)) { return stats.clone(); } } let mut write = self.inner.write(); write - .entry(ip) + .entry((ip, route_key)) .or_insert_with(|| Arc::new(Mutex::new(PingStats::default()))) .clone() } - pub fn record_sent(&self, ip: Ipv4Addr) { - let stats = self.get_or_create(ip); + pub fn record_sent(&self, ip: Ipv4Addr, route_key: RouteKey) { + let stats = self.get_or_create(ip, route_key); stats.lock().sent += 1; } - pub fn record_received(&self, ip: Ipv4Addr) { - let stats = self.get_or_create(ip); - stats.lock().received += 1; + pub fn record_received(&self, ip: Ipv4Addr, route_key: RouteKey) -> f64 { + let stats = self.get_or_create(ip, route_key); + let mut guard = stats.lock(); + guard.received += 1; + + // 计算并返回丢包率 + if guard.sent > 0 { + 1.0 - (guard.received as f64 / guard.sent as f64) + } else { + 0.0 + } } - pub fn get_loss_info(&self, ip: &Ipv4Addr) -> Option { + pub fn get_loss_info(&self, ip: &Ipv4Addr, route_key: &RouteKey) -> Option { let read = self.inner.read(); - read.get(ip).map(|stats| { + read.get(&(*ip, *route_key)).map(|stats| { let guard = stats.lock(); let loss_rate = if guard.sent > 0 { 1.0 - (guard.received as f64 / guard.sent as f64) @@ -155,6 +166,7 @@ impl PacketLossStats { }; PacketLossInfo { ip: *ip, + route_key: Some(*route_key), sent: guard.sent, received: guard.received, loss_rate, @@ -162,10 +174,12 @@ impl PacketLossStats { }) } - pub fn get_all_loss_info(&self) -> Vec { + /// 获取指定 IP 的所有路由的丢包信息 + pub fn get_loss_info_by_ip(&self, ip: &Ipv4Addr) -> Vec { let read = self.inner.read(); read.iter() - .map(|(ip, stats)| { + .filter(|((addr, _), _)| addr == ip) + .map(|((addr, route_key), stats)| { let guard = stats.lock(); let loss_rate = if guard.sent > 0 { 1.0 - (guard.received as f64 / guard.sent as f64) @@ -173,7 +187,8 @@ impl PacketLossStats { 0.0 }; PacketLossInfo { - ip: *ip, + ip: *addr, + route_key: Some(*route_key), sent: guard.sent, received: guard.received, loss_rate, @@ -182,9 +197,64 @@ impl PacketLossStats { .collect() } - pub fn reset(&self, ip: &Ipv4Addr) { + /// 获取指定 IP 的聚合丢包信息(所有路由合并) + pub fn get_aggregated_loss_info(&self, ip: &Ipv4Addr) -> Option { let read = self.inner.read(); - if let Some(stats) = read.get(ip) { + let mut total_sent = 0u64; + let mut total_received = 0u64; + let mut found = false; + + for ((addr, _), stats) in read.iter() { + if addr == ip { + found = true; + let guard = stats.lock(); + total_sent += guard.sent; + total_received += guard.received; + } + } + + if found { + let loss_rate = if total_sent > 0 { + 1.0 - (total_received as f64 / total_sent as f64) + } else { + 0.0 + }; + Some(PacketLossInfo { + ip: *ip, + route_key: None, + sent: total_sent, + received: total_received, + loss_rate, + }) + } else { + None + } + } + + pub fn get_all_loss_info(&self) -> Vec { + let read = self.inner.read(); + read.iter() + .map(|((ip, route_key), stats)| { + let guard = stats.lock(); + let loss_rate = if guard.sent > 0 { + 1.0 - (guard.received as f64 / guard.sent as f64) + } else { + 0.0 + }; + PacketLossInfo { + ip: *ip, + route_key: Some(*route_key), + sent: guard.sent, + received: guard.received, + loss_rate, + } + }) + .collect() + } + + pub fn reset(&self, ip: &Ipv4Addr, route_key: &RouteKey) { + let read = self.inner.read(); + if let Some(stats) = read.get(&(*ip, *route_key)) { *stats.lock() = PingStats::default(); } } diff --git a/vnt-core/src/tunnel_core/p2p/inbound.rs b/vnt-core/src/tunnel_core/p2p/inbound.rs index 9a4e61c..b39209b 100644 --- a/vnt-core/src/tunnel_core/p2p/inbound.rs +++ b/vnt-core/src/tunnel_core/p2p/inbound.rs @@ -201,11 +201,17 @@ impl P2pInboundHandler { let time = i64::from_be_bytes(net_packet.payload()[..8].try_into()?); let now = crate::utils::time::now_ts_ms(); if now >= time { + // 记录接收并获取丢包率 + let loss_rate_f64 = self + .packet_loss_stats + .record_received(ctx.src_ip, route_key); + // 转换为万分率 + let loss_rate = (loss_rate_f64 * 10000.0).round() as u16; + self.route_table.add_route( ctx.src_ip, - Route::from(route_key, metric, (now - time) as _), + Route::from_with_loss(route_key, metric, (now - time) as _, loss_rate), ); - self.packet_loss_stats.record_received(ctx.src_ip); } } } diff --git a/vnt-core/src/tunnel_core/p2p/route_table.rs b/vnt-core/src/tunnel_core/p2p/route_table.rs index 1f4b5dd..10669eb 100644 --- a/vnt-core/src/tunnel_core/p2p/route_table.rs +++ b/vnt-core/src/tunnel_core/p2p/route_table.rs @@ -10,6 +10,8 @@ pub struct Route { route_key: RouteKey, metric: u8, rtt: u32, + /// 丢包率,万分率(0-10000,10000 表示 100% 丢包) + loss_rate: u16, } impl Route { pub fn from(route_key: RouteKey, metric: u8, rtt: u32) -> Self { @@ -17,6 +19,15 @@ impl Route { route_key, metric, rtt, + loss_rate: 0, + } + } + pub fn from_with_loss(route_key: RouteKey, metric: u8, rtt: u32, loss_rate: u16) -> Self { + Self { + route_key, + metric, + rtt, + loss_rate, } } pub fn from_default_rt(route_key: RouteKey, metric: u8) -> Self { @@ -24,6 +35,7 @@ impl Route { route_key, metric, rtt: DEFAULT_RTT, + loss_rate: 0, } } pub fn route_key(&self) -> RouteKey { @@ -39,6 +51,9 @@ impl Route { pub fn metric(&self) -> u8 { self.metric } + pub fn loss_rate(&self) -> u16 { + self.loss_rate + } } #[derive(Clone)] diff --git a/vnt-core/src/tunnel_core/p2p/transport/task.rs b/vnt-core/src/tunnel_core/p2p/transport/task.rs index 2eff25b..30fe54a 100644 --- a/vnt-core/src/tunnel_core/p2p/transport/task.rs +++ b/vnt-core/src/tunnel_core/p2p/transport/task.rs @@ -133,12 +133,9 @@ pub async fn ping_all( ping.set_dest_id(id.into()); ping.set_payload(&crate::utils::time::now_ts_ms().to_be_bytes()) .unwrap(); - if socket_manager - .send_to(ping, &route.route_key()) - .await - .is_ok() - { - packet_loss_stats.record_sent(id); + let route_key = route.route_key(); + if socket_manager.send_to(ping, &route_key).await.is_ok() { + packet_loss_stats.record_sent(id, route_key); } } tokio::time::sleep(Duration::from_millis(10)).await; diff --git a/vnt-web/src/service_http.rs b/vnt-web/src/service_http.rs index 8e416e1..21a546e 100644 --- a/vnt-web/src/service_http.rs +++ b/vnt-web/src/service_http.rs @@ -295,6 +295,7 @@ struct HttpRouteDetail { protocol: String, metric: u8, rtt: u32, + loss_rate: u16, } #[derive(Serialize)] @@ -1032,6 +1033,7 @@ async fn get_peers(State(state): State) -> Json) -> Json