Library overview¶
RhpV2.Client is the heart of the project — a small, dependency-free .NET
library that turns the JSON-over-TCP protocol into a strongly-typed,
async-friendly API.
Namespace map¶
| Namespace | Contains |
|---|---|
RhpV2.Client |
RhpClient, exception types, event args. |
RhpV2.Client.Protocol |
Wire-level DTOs, framing, JSON helpers, enums. |
RhpV2.Client.Testing |
MockRhpServer for unit / integration tests. |
The pieces¶
flowchart LR
subgraph "your app"
A[RhpClient]
end
A -- "TCP, length-prefixed JSON" --> B[XRouter / MockRhpServer]
A -. events .- E((Received\nAccepted\nStatusChanged\nClosed))
RhpClientowns a single TCP connection.- Each request method (
OpenAsync,SendOnHandleAsync, …) auto-assigns anid, awaits the matching reply, and surfaces non-zero error codes asRhpServerException. - Server-pushed messages (RECV / ACCEPT / STATUS / CLOSE) raise events.
- The read loop is fully async; no thread per connection.
Quick tour¶
await using var rhp = await RhpClient.ConnectAsync("xrouter.local");
rhp.Received += (_, e) => Console.WriteLine(e.Message.Data);
var h = await rhp.OpenAsync(
ProtocolFamily.Ax25, SocketMode.Stream,
port: "1", local: "G8PZT", remote: "GB7PZT",
flags: OpenFlags.Active);
await rhp.SendOnHandleAsync(h, "hello\r");
await using var rhp = await RhpClient.ConnectAsync("xrouter.local");
rhp.Accepted += (_, e) =>
Console.WriteLine($"in: {e.Message.Remote} -> child handle {e.Message.Child}");
var listener = await rhp.OpenAsync(
ProtocolFamily.Ax25, SocketMode.Stream,
port: "1", local: "G8PZT", flags: OpenFlags.Passive);
await using var rhp = await RhpClient.ConnectAsync("xrouter.local");
rhp.Received += (_, e) =>
Console.WriteLine($"{e.Message.Action} {e.Message.Srce}->{e.Message.Dest} {e.Message.FrameType}");
var trace = await rhp.OpenAsync(
ProtocolFamily.Ax25, SocketMode.Trace,
port: "1",
flags: OpenFlags.Passive
| OpenFlags.TraceIncoming
| OpenFlags.TraceOutgoing);
Design principles¶
- No third-party dependencies. Pure
System.Text.Json+ sockets. - Async to the core. No blocking calls, no thread-pool starvation.
- Tolerant of spec quirks. Read paths are case-insensitive and
accept both
ConnectReplyandconnectReply. - Forward-compatible. Unknown message types arrive as
UnknownMessage, never as a deserialization error. - Self-contained tests.
MockRhpServerships with the library so downstream consumers can write their own integration tests.