add network test feature (PING/PONG with latency stats)
New frame types PING (0x0B) and PONG (0x0C), each carrying an 8-byte nonce. Server echoes PING nonce verbatim in PONG. Client sends pings at random 10-100ms intervals, correlates nonces to measure RTT. Stats shown live: sent/recv counts, loss %, average latency, jitter (mean absolute delta of consecutive RTTs). Test button toggles on/off. Updated both Rust server (echo in main.rs) and C# client (PingTest.cs, SessionManager routing, MainForm Test button + stats label).
This commit is contained in:
@@ -16,6 +16,8 @@ pub const TYPE_CLOSE: u8 = 0x07;
|
||||
pub const TYPE_UDP_OPEN: u8 = 0x08;
|
||||
pub const TYPE_UDP_DATA: u8 = 0x09;
|
||||
pub const TYPE_UDP_CLOSE: u8 = 0x0A;
|
||||
pub const TYPE_PING: u8 = 0x0B;
|
||||
pub const TYPE_PONG: u8 = 0x0C;
|
||||
|
||||
pub const PROTO_TCP: u8 = 1;
|
||||
pub const PROTO_UDP: u8 = 2;
|
||||
@@ -45,6 +47,8 @@ pub enum Frame {
|
||||
OpenNak { upstream_id: u8, reason: u8 },
|
||||
Data { session_id: u32, payload: Vec<u8> },
|
||||
Close { session_id: u32, reason: Option<u8> },
|
||||
Ping { nonce: u64 },
|
||||
Pong { nonce: u64 },
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -120,6 +124,8 @@ impl Frame {
|
||||
};
|
||||
build(TYPE_CLOSE, *session_id, p)
|
||||
}
|
||||
Frame::Ping { nonce } => build(TYPE_PING, 0, nonce.to_be_bytes().to_vec()),
|
||||
Frame::Pong { nonce } => build(TYPE_PONG, 0, nonce.to_be_bytes().to_vec()),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -212,6 +218,20 @@ impl Frame {
|
||||
};
|
||||
Ok(Frame::Close { session_id, reason })
|
||||
}
|
||||
TYPE_PING => {
|
||||
if payload.len() != 8 {
|
||||
return Err(DecodeError::BadPayload("PING payload must be 8 bytes"));
|
||||
}
|
||||
let nonce = u64::from_be_bytes(payload.try_into().unwrap());
|
||||
Ok(Frame::Ping { nonce })
|
||||
}
|
||||
TYPE_PONG => {
|
||||
if payload.len() != 8 {
|
||||
return Err(DecodeError::BadPayload("PONG payload must be 8 bytes"));
|
||||
}
|
||||
let nonce = u64::from_be_bytes(payload.try_into().unwrap());
|
||||
Ok(Frame::Pong { nonce })
|
||||
}
|
||||
TYPE_UDP_OPEN | TYPE_UDP_DATA | TYPE_UDP_CLOSE => {
|
||||
Err(DecodeError::BadPayload("UDP frame types not implemented in v1"))
|
||||
}
|
||||
|
||||
+6
-1
@@ -207,7 +207,12 @@ async fn handle_frame(
|
||||
Frame::Close { session_id, reason: _ } => {
|
||||
store.lock().expect("store poisoned").remove(&session_id);
|
||||
}
|
||||
Frame::Ping { nonce } => {
|
||||
let pong = Frame::Pong { nonce };
|
||||
let _ = tx.send((src, pong.encode())).await;
|
||||
}
|
||||
// Not expected from a client; ignore.
|
||||
Frame::Manifest { .. } | Frame::OpenAck { .. } | Frame::OpenNak { .. } => {}
|
||||
Frame::Manifest { .. } | Frame::OpenAck { .. } | Frame::OpenNak { .. }
|
||||
| Frame::Pong { .. } => {}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user