gui, flutter: use server rtt for ping display

This commit is contained in:
ospab 2026-05-25 23:00:52 +03:00
parent 164c36ed3e
commit 87540166f6
6 changed files with 26 additions and 8 deletions

View File

@ -36,6 +36,7 @@ pub struct BridgeMetrics {
pub bytes_sent: AtomicU64,
pub bytes_recv: AtomicU64,
pub connection_state: AtomicU8,
pub rtt_ms: portable_atomic::AtomicU32,
}
async fn send_datagram(socket: &crate::transport::Transport, frame: &Bytes, webrtc_masquerade: bool) -> std::io::Result<usize> {
@ -210,6 +211,7 @@ impl Bridge {
RelayMessage::Pong(ts) => {
let now = SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap().as_millis() as u64;
self.last_rtt_ms = now.saturating_sub(ts) as f64;
self.metrics.rtt_ms.store(self.last_rtt_ms as u32, Ordering::Relaxed);
}
RelayMessage::KeepAlive | RelayMessage::Ping(_) | RelayMessage::Connect(_) => {}
}

View File

@ -128,6 +128,7 @@ pub async fn run_client(config: crate::config::ClientConfig) -> Result<()> {
bytes_sent: portable_atomic::AtomicU64::new(0),
bytes_recv: portable_atomic::AtomicU64::new(0),
connection_state: portable_atomic::AtomicU8::new(0),
rtt_ms: portable_atomic::AtomicU32::new(0),
});
let (shutdown_tx, shutdown_rx) = watch::channel(false);

View File

@ -78,6 +78,7 @@ struct MuxConfig {
struct UIMetrics {
bytes_sent: u64,
bytes_recv: u64,
rtt_ms: u32,
}
// ── Messages exchanged with the privileged helper ────────────────────────────
@ -87,7 +88,7 @@ struct UIMetrics {
enum HelperMsg {
Status { value: u8 },
Log { message: String },
Metrics { bytes_sent: u64, bytes_recv: u64 },
Metrics { bytes_sent: u64, bytes_recv: u64, rtt_ms: u32 },
Error { message: String },
}
@ -262,12 +263,14 @@ async fn get_metrics(state: tauri::State<'_, AppState>) -> Result<Option<UIMetri
Some(TunnelHandle::InProcess(s)) => Ok(Some(UIMetrics {
bytes_sent: s.metrics.bytes_sent.load(Ordering::Relaxed),
bytes_recv: s.metrics.bytes_recv.load(Ordering::Relaxed),
rtt_ms: s.metrics.rtt_ms.load(Ordering::Relaxed),
})),
Some(TunnelHandle::Helper(h)) => {
let ps = h.pipe_state.lock().await;
Ok(Some(UIMetrics {
bytes_sent: ps.bytes_sent,
bytes_recv: ps.bytes_recv,
rtt_ms: ps.rtt_ms,
}))
}
}
@ -338,6 +341,7 @@ async fn start_proxy_in_process(
// Start at 1 (connecting) so UI polling doesn't see 0 and flip back to disconnected
// before the handshake task has had a chance to begin.
connection_state: portable_atomic::AtomicU8::new(1),
rtt_ms: portable_atomic::AtomicU32::new(0),
});
let (shutdown_tx, shutdown_rx) = watch::channel(false);
@ -383,7 +387,7 @@ async fn start_tun_via_helper(
}).to_string();
let (cmd_tx, mut cmd_rx) = tokio::sync::mpsc::channel::<String>(16);
let pipe_state = Arc::new(Mutex::new(HelperPipeState { connection_state: 1, bytes_sent: 0, bytes_recv: 0 }));
let pipe_state = Arc::new(Mutex::new(HelperPipeState { connection_state: 1, bytes_sent: 0, bytes_recv: 0, rtt_ms: 0 }));
let state_for_task = pipe_state.clone();
tokio::spawn(async move {
@ -403,7 +407,7 @@ async fn start_tun_via_helper(
let mut s = state_for_task.lock().await;
match msg {
HelperMsg::Status { value } => s.connection_state = value,
HelperMsg::Metrics { bytes_sent, bytes_recv } => { s.bytes_sent = bytes_sent; s.bytes_recv = bytes_recv; }
HelperMsg::Metrics { bytes_sent, bytes_recv, rtt_ms } => { s.bytes_sent = bytes_sent; s.bytes_recv = bytes_recv; s.rtt_ms = rtt_ms; }
HelperMsg::Error { message } => { s.connection_state = 0; eprintln!("Helper error: {}", message); }
_ => {}
}
@ -425,6 +429,7 @@ struct HelperPipeState {
connection_state: u8,
bytes_sent: u64,
bytes_recv: u64,
rtt_ms: u32,
}
fn find_helper_exe() -> Option<PathBuf> {

View File

@ -159,6 +159,11 @@ async function poll() {
if (metrics) {
metricDown.textContent = fmtBytes(metrics.bytes_recv);
metricUp.textContent = fmtBytes(metrics.bytes_sent);
const isTun = rawConfig?.tun?.enable;
const modeStr = isTun ? 'TUN' : 'SOCKS5';
const pingStr = metrics.rtt_ms > 0 ? ` (${metrics.rtt_ms} ms)` : '';
metricMode.textContent = modeStr + pingStr;
}
} catch {
setState('disconnected');

View File

@ -142,6 +142,7 @@ pub extern "system" fn Java_net_ostp_client_OstpClientSdk_startClient(
bytes_sent: portable_atomic::AtomicU64::new(0),
bytes_recv: portable_atomic::AtomicU64::new(0),
connection_state: portable_atomic::AtomicU8::new(0),
rtt_ms: portable_atomic::AtomicU32::new(0),
});
let bridge = match Bridge::new(&config, Arc::clone(&metrics)) {
@ -310,16 +311,17 @@ pub extern "system" fn Java_net_ostp_client_OstpClientSdk_getMetrics(
let sent = m.bytes_sent.load(Ordering::Relaxed);
let recv = m.bytes_recv.load(Ordering::Relaxed);
let conn_state = m.connection_state.load(Ordering::Relaxed);
let rtt = m.rtt_ms.load(Ordering::Relaxed);
let json = format!(
r#"{{"bytes_sent": {}, "bytes_recv": {}, "connection_state": {}}}"#,
sent, recv, conn_state
r#"{{"bytes_sent": {}, "bytes_recv": {}, "connection_state": {}, "rtt_ms": {}}}"#,
sent, recv, conn_state, rtt
);
match env.new_string(json) {
Ok(s) => s.into_raw(),
Err(_) => std::ptr::null_mut(),
}
} else {
match env.new_string(r#"{"bytes_sent": 0, "bytes_recv": 0, "connection_state": 0}"#) {
match env.new_string(r#"{"bytes_sent": 0, "bytes_recv": 0, "connection_state": 0, "rtt_ms": 0}"#) {
Ok(s) => s.into_raw(),
Err(_) => std::ptr::null_mut(),
}

View File

@ -37,7 +37,7 @@ enum GuiCmd {
enum HelperMsg {
Status { value: u8 },
Log { message: String },
Metrics { bytes_sent: u64, bytes_recv: u64 },
Metrics { bytes_sent: u64, bytes_recv: u64, rtt_ms: u32 },
Error { message: String },
}
@ -144,6 +144,7 @@ async fn run_server() -> Result<()> {
bytes_sent: portable_atomic::AtomicU64::new(0),
bytes_recv: portable_atomic::AtomicU64::new(0),
connection_state: portable_atomic::AtomicU8::new(0),
rtt_ms: portable_atomic::AtomicU32::new(0),
});
let (shutdown_tx, shutdown_rx) = watch::channel(false);
@ -179,13 +180,15 @@ async fn run_server() -> Result<()> {
let sent = metrics_tick.bytes_sent.load(Ordering::Relaxed);
let recv = metrics_tick.bytes_recv.load(Ordering::Relaxed);
let rtt = metrics_tick.rtt_ms.load(Ordering::Relaxed);
let mut w = writer_tick.lock().await;
if cs != last_state {
last_state = cs;
let json = serde_json::to_string(&HelperMsg::Status { value: cs }).unwrap_or_default();
if w.write_all(format!("{}\n", json).as_bytes()).await.is_err() { break; }
}
let json = serde_json::to_string(&HelperMsg::Metrics { bytes_sent: sent, bytes_recv: recv }).unwrap_or_default();
let json = serde_json::to_string(&HelperMsg::Metrics { bytes_sent: sent, bytes_recv: recv, rtt_ms: rtt }).unwrap_or_default();
if w.write_all(format!("{}\n", json).as_bytes()).await.is_err() { break; }
drop(w);
}