Pacem.Extensions.Sockets
0.10.15-euler
See the version list below for details.
dotnet add package Pacem.Extensions.Sockets --version 0.10.15-euler
NuGet\Install-Package Pacem.Extensions.Sockets -Version 0.10.15-euler
<PackageReference Include="Pacem.Extensions.Sockets" Version="0.10.15-euler" />
<PackageVersion Include="Pacem.Extensions.Sockets" Version="0.10.15-euler" />
<PackageReference Include="Pacem.Extensions.Sockets" />
paket add Pacem.Extensions.Sockets --version 0.10.15-euler
#r "nuget: Pacem.Extensions.Sockets, 0.10.15-euler"
#:package Pacem.Extensions.Sockets@0.10.15-euler
#addin nuget:?package=Pacem.Extensions.Sockets&version=0.10.15-euler&prerelease
#tool nuget:?package=Pacem.Extensions.Sockets&version=0.10.15-euler&prerelease
Pacem.Extensions.Sockets
A small, dependable TCP/IP client connection for long-lived links to appliances that speak a line- or frame-oriented protocol over a raw socket — domotic gateways, PLCs, meters.
It gives you one thing well: a connection that stays up. Connect, send, receive, and — in keep-alive mode — reconnect on its own when the peer disappears, without spinning the CPU and without ever blocking the caller waiting for an appliance to come back.
Install
dotnet add package Pacem.Extensions.Sockets
Targets net10.0. The only dependency is
Microsoft.Extensions.DependencyInjection.Abstractions.
Quick start
services.AddPacemTcpIp();
public sealed class MeterReader(ITcpIpConnectionFactory factory) : IDisposable
{
private readonly ITcpIpConnection _connection = factory.CreateConnection(options =>
{
options.Host = "172.24.0.100";
options.Port = 20000;
options.Mode = TcpIpConnectionMode.KeepAlive;
options.OnConnectedAsync = () => { /* handshake */ return Task.CompletedTask; };
options.OnErrorAsync = exc => { /* log */ return Task.CompletedTask; };
});
public async Task StartAsync()
{
await _connection.OpenAsync();
if (_connection.IsConnected)
{
_connection.StartReceiving(bytes => Handle(bytes));
}
}
public void Dispose() => _connection.Dispose();
}
Connection modes
| Mode | Behaviour |
|---|---|
Default |
One connection attempt. A dropped link stays dropped. |
KeepAlive |
Liveness beat, plus automatic reconnection with exponential back-off. |
OpenAsync never waits for an absent peer
In KeepAlive mode OpenAsync makes one attempt and returns. If it fails, retries
continue in the background and the task still completes — so a request handler calling into
this never hangs because an appliance is offline.
Always check IsConnected after awaiting it:
await connection.OpenAsync();
if (!connection.IsConnected)
{
// not fatal: the background loop keeps trying
}
Back-off starts at ReconnectDelay, doubles per failure, and is capped at
MaxReconnectDelay. CloseAsync() and Dispose() stop it for good; a later OpenAsync()
starts over.
Receiving: framing is yours
StartReceiving hands you buffers exactly as they come off the socket. TCP is a byte
stream with no message boundaries, so one read may carry half a message, or five of them.
Re-assembling is the caller's job — the connection will not guess where your messages end.
connection.StartReceiving(chunk =>
{
foreach (var message in _framer.Push(chunk)) // your framing
{
Handle(message);
}
});
Reset your framer on OnConnectedAsync / OnDisconnectedAsync: a reconnection restarts the
byte stream and any half-received message is void.
Calling StartReceiving twice without an intervening StopReceiving is a no-op — a second
pump would steal bytes from the first.
Options
| Option | Default | Notes |
|---|---|---|
Host, Port |
— | Required. |
Mode |
Default |
See above. |
UseSsl |
false |
Certificate validation is strict; self-signed peers are rejected. |
ConnectTimeout |
10 s | Per attempt. |
KeepAliveTimespan |
10 s | Liveness period; floored at 1 s. |
KeepAliveContent |
null |
Optional bytes sent on every beat. |
ReconnectDelay |
1 s | First back-off step. |
MaxReconnectDelay |
30 s | Back-off ceiling. |
SendBufferSize, ReceiveBufferSize |
8192 | Socket buffers. |
Handlers
OnConnectedAsync, OnDisconnectedAsync, OnCloseAsync, OnReceivedAsync, OnErrorAsync.
Handlers are awaited on the connection's own loops. A handler that throws is reported through
OnErrorAsync and never takes the connection down — but a slow handler does hold up the
receive pump, so offload real work.
OnDisconnectedAsync fires on both an unsolicited drop and an explicit CloseAsync;
OnCloseAsync fires only on the latter.
Behaviour worth knowing
- Sends are serialized. A keep-alive beat cannot interleave with an application write, so frames never get spliced together on the wire.
SendAsyncon a closed connection throwsInvalidOperationException. A failure during a send is reported viaOnErrorAsyncand treated as a drop instead.- Liveness is detected by polling for a readable socket with nothing to read, which is what actually catches a peer that went away.
Disposeis idempotent and stops everything: pump, beat, reconnection.
Threading
One connection instance is safe to use from several callers: SendAsync serializes,
StartReceiving / StopReceiving / CloseAsync / Dispose are guarded. The registered
ITcpIpConnectionFactory is a singleton; connections themselves are not pooled — you own
each one you create, and you dispose it.
License
Apache 2.0.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated | |
|---|---|---|---|
| 0.10.19-cardano | 170 | 10/26/2026 | |
| 0.10.19-bombelli | 122 | 10/20/2026 | |
| 0.10.18 | 120 | 10/14/2026 | |
| 0.10.18-venn | 116 | 10/9/2026 | |
| 0.10.18-turing | 124 | 10/8/2026 | |
| 0.10.18-selberg | 127 | 10/1/2026 | |
| 0.10.18-riemann | 127 | 9/26/2026 | |
| 0.10.18-pascal | 131 | 9/21/2026 | |
| 0.10.17 | 133 | 9/18/2026 | |
| 0.10.17-foucault | 130 | 9/18/2026 | |
| 0.10.16 | 128 | 9/16/2026 | |
| 0.10.16-hamilton | 118 | 9/14/2026 | |
| 0.10.16-gauss | 127 | 9/14/2026 | |
| 0.10.16-faraday | 136 | 9/9/2026 | |
| 0.10.15 | 140 | 9/7/2026 | |
| 0.10.15-euler | 146 | 9/7/2026 | |
| 0.10.14 | 126 | 9/6/2026 | |
| 0.10.14-dedekind | 144 | 9/6/2026 | |
| 0.10.14-cauchy | 135 | 9/5/2026 | |
| 0.10.14-boltzmann | 122 | 9/5/2026 |