TCP networking (server + client)
Stand up a TCP server, subscribe to its lifecycle and data events, then round-trip a message from a client.
What you'll build
A SquidTcpServer (SquidStd.Network) bound to a loopback endpoint that logs client connections and received
payloads, plus a SquidStdTcpClient that connects and sends bytes. The server run is guarded behind --run so
dotnet run returns immediately by default.
Prerequisites
- .NET 10 SDK
dotnet add package SquidStd.Network- A free local TCP port (the sample uses 9099 on loopback)
Steps
1. Create the server and subscribe to events
Construct SquidTcpServer with an IPEndPoint, then subscribe to OnClientConnect and OnDataReceived. The data
event carries the source client and the payload as ReadOnlyMemory<byte>.
var endPoint = new IPEndPoint(IPAddress.Loopback, 9099);
var server = new SquidTcpServer(endPoint);
server.OnClientConnect += (_, args) =>
Console.WriteLine($"Client connected: session {args.Client.SessionId}");
server.OnDataReceived += (_, args) =>
Console.WriteLine(
$"Received {args.Data.Length} byte(s): {Encoding.UTF8.GetString(args.Data.Span)}"
);
2. Start the server and connect a client
StartAsync begins accepting connections. SquidStdTcpClient.ConnectAsync opens an outbound connection and starts
its receive loop; SendAsync writes the payload, which surfaces on the server's OnDataReceived.
await server.StartAsync(CancellationToken.None);
var client = await SquidStdTcpClient.ConnectAsync(endPoint);
await client.SendAsync(Encoding.UTF8.GetBytes("hello squid"), CancellationToken.None);
await Task.Delay(200);
await client.DisposeAsync();
await server.DisposeAsync();
Run it
dotnet run --project samples/SquidStd.Samples.Networking -- --run
Without --run the program just prints a hint and exits. With --run it prints the connecting session and the
received message (hello squid).
How it works
SquidTcpServer recreates its listening socket on every StartAsync, so Stop/Start cycles are supported. Each
accepted socket is wrapped in a SquidStdTcpClient whose events (OnConnected, OnDataReceived, OnDisconnected,
OnException) are re-raised by the server. An optional INetFramer lets you emit one OnDataReceived per logical
frame instead of per socket read, and INetMiddleware components can transform inbound and outbound payloads.