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:
@@ -17,6 +17,8 @@ static class Proto
|
||||
public const byte TypeOpenNak = 0x05;
|
||||
public const byte TypeData = 0x06;
|
||||
public const byte TypeClose = 0x07;
|
||||
public const byte TypePing = 0x0B;
|
||||
public const byte TypePong = 0x0C;
|
||||
|
||||
public const byte ProtoTcp = 1;
|
||||
public const byte ProtoUdp = 2;
|
||||
@@ -46,6 +48,8 @@ abstract record Frame
|
||||
internal record OpenNak(byte UpstreamId, byte Reason) : Frame;
|
||||
internal record Data(uint SessionId, byte[] Payload) : Frame;
|
||||
internal record Close(uint SessionId, byte? Reason) : Frame;
|
||||
internal record Ping(ulong Nonce) : Frame;
|
||||
internal record Pong(ulong Nonce) : Frame;
|
||||
}
|
||||
|
||||
static class FrameCodec
|
||||
@@ -86,6 +90,10 @@ static class FrameCodec
|
||||
Frame.Close close =>
|
||||
Build(Proto.TypeClose, close.SessionId,
|
||||
close.Reason.HasValue ? [close.Reason.Value] : []),
|
||||
Frame.Ping ping =>
|
||||
Build(Proto.TypePing, 0, EncodeNonce(ping.Nonce)),
|
||||
Frame.Pong pong =>
|
||||
Build(Proto.TypePong, 0, EncodeNonce(pong.Nonce)),
|
||||
_ => throw new InvalidOperationException($"unknown frame type: {frame.GetType()}"),
|
||||
};
|
||||
}
|
||||
@@ -138,12 +146,34 @@ static class FrameCodec
|
||||
new Frame.Data(sessionId, payload.ToArray()),
|
||||
Proto.TypeClose when payload.Length is 0 or 1 =>
|
||||
new Frame.Close(sessionId, payload.Length == 1 ? payload[0] : null),
|
||||
Proto.TypePong when payload.Length == 8 =>
|
||||
new Frame.Pong(ParseNonce(payload)),
|
||||
Proto.TypePing when payload.Length == 8 =>
|
||||
new Frame.Ping(ParseNonce(payload)),
|
||||
Proto.TypeDiscover when payload.Length == 0 =>
|
||||
new Frame.Discover(),
|
||||
_ => null,
|
||||
};
|
||||
}
|
||||
|
||||
static ulong ParseNonce(ReadOnlySpan<byte> payload)
|
||||
{
|
||||
ulong nonce = 0;
|
||||
for (int i = 0; i < 8; i++)
|
||||
nonce = (nonce << 8) | payload[i];
|
||||
return nonce;
|
||||
}
|
||||
|
||||
static byte[] EncodeNonce(ulong nonce)
|
||||
{
|
||||
return [
|
||||
(byte)(nonce >> 56), (byte)(nonce >> 48),
|
||||
(byte)(nonce >> 40), (byte)(nonce >> 32),
|
||||
(byte)(nonce >> 24), (byte)(nonce >> 16),
|
||||
(byte)(nonce >> 8), (byte)(nonce & 0xFF),
|
||||
];
|
||||
}
|
||||
|
||||
static Frame.Manifest? ParseManifest(ReadOnlySpan<byte> payload)
|
||||
{
|
||||
if (payload.Length < 1)
|
||||
|
||||
Reference in New Issue
Block a user