PicoNode.Abs
2026.1.4
dotnet add package PicoNode.Abs --version 2026.1.4
NuGet\Install-Package PicoNode.Abs -Version 2026.1.4
<PackageReference Include="PicoNode.Abs" Version="2026.1.4" />
<PackageVersion Include="PicoNode.Abs" Version="2026.1.4" />
<PackageReference Include="PicoNode.Abs" />
paket add PicoNode.Abs --version 2026.1.4
#r "nuget: PicoNode.Abs, 2026.1.4"
#:package PicoNode.Abs@2026.1.4
#addin nuget:?package=PicoNode.Abs&version=2026.1.4
#tool nuget:?package=PicoNode.Abs&version=2026.1.4
PicoNode
A layered, AOT-native networking stack for .NET — from raw TCP/UDP sockets to a fully featured HTTP web framework.
English | 简体中文 | 繁體中文 | Deutsch | Español | Français | 日本語 | Português (Brasil) | Русский
┌─────────────────────────────────────────────────────────────┐
│ PicoNode: layered networking for .NET │
│ ✓ Raw TCP/UDP socket transports with async I/O │
│ ✓ HTTP/1.1 + HTTP/2 + WebSocket protocols │
│ ✓ Web framework with middleware, routing, static files │
│ ✓ Integrated with PicoHex ecosystem (PicoDI/PicoLog/PicoCfg)│
│ ✓ Native AOT compatible across all net10.0 layers │
│ ✓ Minimal runtime dependencies │
└─────────────────────────────────────────────────────────────┘
Why PicoNode?
| Feature | PicoNode | ASP.NET Core |
|---|---|---|
| Dependency model | Zero required runtime deps; layer pick-and-choose | Microsoft.AspNetCore.App framework reference |
| Request parsing | Span-based streaming, zero-copy System.IO.Pipelines |
String-based with IO.Pipelines adapter |
| HTTP/2 | Inline HPACK decoder, frame-level control | Transparent via Kestrel; limited low-level access |
| AOT Support | ✅ Native — all net10.0 libraries | ⚠️ Requires trimming |
| DI / Logging / Config | PicoDI + PicoLog + PicoCfg (PicoHex native) | Microsoft.Extensions.* |
| WebSocket | RFC 6455 frame codec with message handler abstraction | Transparent via middleware |
| Line count | ~15K for the full stack | ~1M+ for ASP.NET Core |
Design priority: PicoNode prioritizes allocation efficiency and AOT compatibility.
ValueTaskon hot-path delegates, ArrayPool-based buffer management, and optional delegates (no forced allocations) are deliberate trade-offs — they keep the transport layer compact and predictable.
The PicoHex Ecosystem
PicoNode is part of the PicoHex family and integrates natively with:
| Library | Purpose | NuGet |
|---|---|---|
| PicoDI | Zero-reflection compile-time DI | PicoDI.Abs |
| PicoLog | Structured logging with AOT safety | PicoLog.Abs |
| PicoCfg | Source-generated configuration binding | PicoCfg.Abs |
PicoNode.Abs Core interfaces (netstandard2.0, zero deps)
↓
PicoNode TCP & UDP transports + ILogger (net10.0)
↓
PicoNode.Http HTTP/1.1 + HTTP/2 + WebSocket (net10.0)
↓
PicoNode.Web Web framework + PicoDI ISvcContainer (net10.0)
↓
PicoWeb Ready-to-run web server + PicoCfg (net10.0)
Quick Start
Installation
dotnet add package PicoNode
Installing
PicoNodebrings in the TCP/UDP transport. ReferencePicoNode.HttporPicoNode.Webfor higher-level layers.
Package Architecture
PicoNode ships as layered NuGet packages. Pick exactly the abstraction level you need:
| Package | Install when… | What you get |
|---|---|---|
| PicoWeb | You want a ready-to-run web server | WebServer + WebApp + HTTP + TCP (all transitive) |
| PicoNode.Web | You want the web framework without hosting | WebApp, routing, middleware, static files, DI |
| PicoNode.Http | You want raw HTTP protocol handling | HTTP/1.1 + HTTP/2 + WebSocket, HttpRouter |
| PicoNode | You want raw TCP/UDP transports | TcpNode, UdpNode, socket lifecycle, metrics |
| PicoNode.Abs | You're writing a handler or extension | INode, ITcpConnectionHandler, core contracts |
PicoWeb → PicoNode.Web → PicoNode.Http → PicoNode → PicoNode.Abs
(host) (web/DI) (HTTP) (transport) (interfaces)
TCP Echo Server
using System.Net;
using PicoNode;
using PicoNode.Abs;
var node = new TcpNode(new TcpNodeOptions
{
Endpoint = new IPEndPoint(IPAddress.Loopback, 7001),
ConnectionHandler = new EchoHandler(),
});
await node.StartAsync();
Console.ReadLine();
await node.DisposeAsync();
sealed class EchoHandler : ITcpConnectionHandler
{
public Task OnConnectedAsync(ITcpConnectionContext c, CancellationToken ct)
=> Task.CompletedTask;
public Task OnClosedAsync(ITcpConnectionContext c, TcpCloseReason r,
Exception? e, CancellationToken ct) => Task.CompletedTask;
public ValueTask<SequencePosition> OnReceivedAsync(
ITcpConnectionContext connection,
ReadOnlySequence<byte> buffer,
CancellationToken ct)
{
_ = connection.SendAsync(buffer, ct);
return ValueTask.FromResult(buffer.End);
}
}
HTTP Server (Low-Level)
using System.Net;
using PicoNode;
using PicoNode.Http;
var node = new TcpNode(new TcpNodeOptions
{
Endpoint = new IPEndPoint(IPAddress.Loopback, 7002),
ConnectionHandler = new HttpConnectionHandler(new HttpConnectionHandlerOptions
{
RequestHandler = new HttpRouter(new HttpRouterOptions
{
Routes =
[
HttpRoute.MapGet("/", static (_, _) =>
ValueTask.FromResult(new HttpResponse
{
StatusCode = 200, ReasonPhrase = "OK",
Headers = [new("Content-Type", "text/plain")],
Body = "Hello from PicoNode.Http"u8.ToArray(),
})),
],
}).HandleAsync,
ServerHeader = "PicoNode",
}),
});
await node.StartAsync();
Console.ReadLine();
await node.DisposeAsync();
Web Application (DI First + Delegate)
using PicoNode.Web;
using PicoWeb;
var api = new WebApiBuilder()
.ConfigureApp(o => o.ServerHeader = "MyApp")
.RegisterScoped<IUserService, UserService>()
.Build();
api.MapGet("/", (WebContext ctx) =>
Results.Text(200, "Hello, World!"));
api.MapGet("/users/{id}", async (WebContext ctx, IUserService svc) =>
{
var user = await svc.GetByIdAsync(ctx.RouteValues["id"]);
var bytes = PicoJetson.JsonSerializer.SerializeToUtf8Bytes(user);
return Results.Json(200, bytes);
});
api.MapPost("/echo", async (WebContext ctx) =>
{
using var reader = new StreamReader(ctx.Request.BodyStream);
var body = await reader.ReadToEndAsync();
return Results.Text(200, body);
});
await api.RunAsync("http://+:8080");
Web Application (Controller-based)
// Controllers/UsersController.cs
using PicoJetson;
public class UsersController
{
public UserDto GetUser(int id) { return new UserDto { Id = id }; }
}
// Program.cs
var api = new WebApiBuilder()
.RegisterScoped<UsersController>()
.Build();
// Controllers.Gen auto-generates endpoint stubs + [PicoJsonSerializable]
await api.RunAsync("http://+:8080");
Configuration
PicoNode supports two configuration modes:
Code-First (inline)
var options = new TcpNodeOptions
{
Endpoint = new IPEndPoint(IPAddress.Any, 8080),
MaxConnections = 500,
IdleTimeout = TimeSpan.FromMinutes(5),
};
var node = new TcpNode(options);
PicoCfg Binding (AOT-safe, source-generated)
var config = await Cfg.CreateBuilder()
.Add(new Dictionary<string, string>
{
["App:Name"] = "PicoCfg",
["App:Enabled"] = "true",
})
.BuildAsync();
var settings = CfgBind.Bind<AppSettings>(config, "App");
public sealed class AppSettings
{
public string? Name { get; set; }
public bool Enabled { get; set; }
}
Runtime Reload
// TcpNode supports runtime config reload (except Endpoint)
var options = new TcpNodeOptions
{
Endpoint = new IPEndPoint(IPAddress.Loopback, 8080),
Config = config, // ICfgRoot for live reload
};
// Node starts a reload loop watching for config changes
Key Options
TcpNodeOptions
| Option | Default | Description |
|---|---|---|
Endpoint |
(required) | Local endpoint to bind |
ConnectionHandler |
(required) | ITcpConnectionHandler |
MaxConnections |
1000 | Maximum concurrent connections |
IdleTimeout |
2 min | Time before idle connections are closed |
DrainTimeout |
5 sec | Grace period on shutdown |
SslOptions |
null |
TLS/SSL configuration |
NoDelay |
true |
TCP_NODELAY (Nagle disabled) |
Logger |
null |
PicoLog ILogger for structured diagnostics |
UdpNodeOptions
| Option | Default | Description |
|---|---|---|
Endpoint |
(required) | Local endpoint to bind |
DatagramHandler |
(required) | IUdpDatagramHandler |
DispatchWorkerCount |
1 | Concurrent datagram workers |
DatagramQueueCapacity |
1024 | Per-worker queue depth |
QueueOverflowMode |
DropNewest |
Behavior when queues are full |
Logger |
null |
PicoLog ILogger |
HttpConnectionHandlerOptions
| Option | Default | Description |
|---|---|---|
RequestHandler |
(required) | HttpRequestHandler delegate |
ServerHeader |
null |
Value for the Server header |
MaxRequestBytes |
8192 | Maximum request size in bytes |
Logger |
null |
PicoLog ILogger |
Logging
PicoNode uses PicoLog for structured diagnostics. All non-fatal errors are logged with operation context:
var logger = new LoggerFactory([new ConsoleSink()])
.CreateLogger("PicoNode.Tcp");
var node = new TcpNode(new TcpNodeOptions
{
Endpoint = new IPEndPoint(IPAddress.Loopback, 7001),
ConnectionHandler = handler,
Logger = logger, // All transport faults logged here
});
// Log output:
// [Error] Operation tcp.accept failed: AcceptFailed - System.Net.Sockets.SocketException
// [Warning] Operation tcp.reject.limit failed: SessionRejected
// [Debug] Socket shutdown during TLS teardown failed
Log levels by fault code:
Error: StartFailed, StopFailed, AcceptFailed, ReceiveFailed, SendFailed, HandlerFailed, TlsFailed, DatagramReceiveFailed, DatagramHandlerFailedWarning: SessionRejected, DatagramDroppedDebug: Socket shutdown during cleanup (best-effort operations)
Dependency Injection
PicoNode.Web requires ISvcContainer at construction time (DI First). Scopes are created per-request automatically.
Manual DI resolution in handlers
using PicoNode.Web;
using PicoWeb;
using PicoJetson;
var container = new SvcContainer();
container.RegisterScoped<IDatabase, SqlDatabase>();
var app = new WebApp(container);
app.MapGet("/db", async (WebContext ctx) =>
{
var db = ctx.Services.GetService<IDatabase>() as IDatabase;
var data = await db!.QueryAsync("...");
var bytes = PicoJetson.JsonSerializer.SerializeToUtf8Bytes(data);
return Results.Json(200, bytes);
});
app.Build();
Auto-parameter injection via Delegate
Handler parameters are automatically resolved (requires using PicoNode.Web;):
WebContext→ current contextCancellationToken→ request cancellation token- Any registered service → resolved from DI scope
app.MapGet("/users/{id}", async (WebContext ctx, IUserService svc) =>
{
var user = await svc.GetByIdAsync(ctx.RouteValues["id"]);
var bytes = PicoJetson.JsonSerializer.SerializeToUtf8Bytes(user);
return Results.Json(200, bytes);
});
AOT-compatible serialization
PicoJetson source generators run at compile time. Handlers must call SerializeToUtf8Bytes<T>() directly in user code to trigger generator:
// ✅ Triggers PicoJetson.Gen — UserDto serializer generated
var bytes = PicoJetson.JsonSerializer.SerializeToUtf8Bytes(user);
// ❌ Does NOT trigger generator (cross-assembly generic)
Results.Json<UserDto>(200, user);
WebApiBuilder (convenience)
using PicoNode.Web;
using PicoWeb;
var api = new WebApiBuilder()
.RegisterScoped<IUserService, UserService>()
.ConfigureJson(o => o.PropertyNamingPolicy = JsonNamingPolicy.CamelCase)
.Build();
api.MapGet("/api/users/{id}", async (WebContext ctx, IUserService svc) =>
{
var user = await svc.GetByIdAsync(ctx.RouteValues["id"]);
var bytes = PicoJetson.JsonSerializer.SerializeToUtf8Bytes(user);
return Results.Json(200, bytes);
});
await api.RunAsync("http://+:5000");
WebApiBuilder with Controllers (three-way endpoint registration)
// 1. Controller in Controllers/ folder (convention)
// Controllers/UsersController.cs
public class UsersController
{
public UserDto GetUser(int id) { return new UserDto { ... }; }
public List<UserDto> GetAllUsers() { return ...; }
}
// 2. Register controller in DI
builder.RegisterScoped<UsersController>();
// 3. Call EndpointRegistrar (auto-generated by Controllers.Gen)
EndpointRegistrar.RegisterAll(app);
// 4. Or use WebApiBuilder (calls it automatically)
new WebApiBuilder()
.RegisterScoped<UsersController>()
.Build()
.RunAsync("http://+:5000");
Controllers.Gen and PicoWeb.Gen source generators:
- Scan
Controllers/folder andapp.MapGet/MapPostcalls - Generate
[PicoJsonSerializable]for discovered DTOs - Generate endpoint stubs that resolve controllers from DI
Note: The controller-based pattern requires PicoJetson.Gen for automatic DTO serialization registration. For the MapXX pattern, call
PicoJetson.JsonSerializer.SerializeToUtf8Bytes<T>()explicitly in your handler.
## Built-in Middleware
### Compression
```csharp
var compression = new CompressionMiddleware(
CompressionLevel.Fastest, minimumBodySize: 860);
app.Use(compression.InvokeAsync);
Supports Brotli, Gzip, and Deflate. Auto-selects the best encoding from the client's Accept-Encoding header.
Static Files
var staticFiles = new StaticFileMiddleware(
"/path/to/wwwroot", requestPathPrefix: "/static");
app.Use(staticFiles.InvokeAsync);
Serves files from a root directory. Prevents directory traversal. Maps 30+ file extensions to MIME types.
CORS
app.Use(async (ctx, next, ct) =>
{
var corsOptions = new CorsOptions
{
AllowedOrigins = ["https://example.com"],
AllowedMethods = ["GET", "POST"],
AllowCredentials = true,
};
var preflight = CorsHandler.HandlePreflight(ctx.Request, corsOptions);
if (preflight is not null)
return preflight;
var response = await next(ctx, ct);
// Add CORS response headers
foreach (var header in CorsHandler.GetResponseHeaders(ctx.Request, corsOptions))
{
response.Headers.Add(header.Key, header.Value);
}
return response;
});
Cookies & Multipart
// Cookie parsing
var cookies = CookieParser.Parse(context.Request.HeaderFields);
// Set-Cookie
var setCookie = new SetCookieBuilder("session", "abc123")
.Path("/").HttpOnly().Secure().SameSite("Strict").MaxAge(3600)
.Build();
// Multipart form data
var form = MultipartFormDataParser.Parse(context.Request);
foreach (var field in form?.Fields ?? [])
Console.WriteLine($"{field.Name} = {field.Value}");
foreach (var file in form?.Files ?? [])
Console.WriteLine($"{file.FileName}: {file.ContentType} ({file.Content.Length} bytes)");
Metrics
Both TcpNode and UdpNode expose real-time counters:
// TCP
var tcpMetrics = node.GetMetrics(); // only TcpNode
Console.WriteLine($"Accepted: {tcpMetrics.TotalAccepted}");
Console.WriteLine($"Active: {tcpMetrics.ActiveConnections}");
Console.WriteLine($"Sent: {tcpMetrics.TotalBytesSent}");
Console.WriteLine($"Received: {tcpMetrics.TotalBytesReceived}");
// UDP counters available via internal state
// (UdpNode tracks datagrams, bytes, and drops internally)
## Projects
| Project | Target | Description |
|---------|--------|-------------|
| **PicoNode.Abs** | netstandard2.0 | Core interfaces: `INode`, `ITcpConnectionHandler`, `IUdpDatagramHandler`, fault codes, enums |
| **PicoNode** | net10.0 | `TcpNode` and `UdpNode` — production-grade async socket transports |
| **PicoNode.Http** | net10.0 | `HttpConnectionHandler`, `HttpRouter` — HTTP/1.1, HTTP/2, WebSocket |
| **PicoNode.Web** | net10.0 | `WebApp`, `WebRouter`, middleware, static files, compression, CORS, DI |
| **PicoWeb** | net10.0 | `WebServer` — thin host wiring `WebApp` to `TcpNode` |
## Samples
| Sample | Port | Description |
|--------|------|-------------|
| `PicoNode.Samples.Echo` | 7001 (TCP), 7002 (UDP) | Raw TCP/UDP echo server |
| `PicoNode.Samples.Http` | 7003 | HTTP routing with `HttpRouter` |
| `PicoWeb.Samples` | 7004 | Full web app with middleware and DI |
```bash
dotnet run --project samples/PicoWeb.Samples/PicoWeb.Samples.csproj
Building & Testing
# Build the entire solution
dotnet build PicoNode.slnx -c Release
# Run all tests
dotnet test --solution PicoNode.slnx -c Release
# Run a specific test project
dotnet test --project tests/PicoNode.Http.Tests/PicoNode.Http.Tests.csproj -c Release
# AOT publish check
dotnet publish src/PicoWeb/PicoWeb.csproj -c Release -r win-x64 -p:PublishAot=true
Benchmarks
Microbenchmarks are provided via PicoBench:
dotnet run --project benchmarks/PicoNode.Http.Benchmarks/PicoNode.Http.Benchmarks.csproj -c Release -- quick
Benchmarks cover HTTP parsing, router dispatch (hit/miss/405), full pipeline, and localhost round-trips.
Requirements
- .NET 10.0+ (PicoNode, PicoNode.Http, PicoNode.Web, PicoWeb)
- .NET Standard 2.0 (PicoNode.Abs — maximum compatibility)
- PicoHex ecosystem (optional): PicoDI, PicoLog, PicoCfg
License
MIT © 2025 XiaoFei Du
<p align="center"> <b>PicoNode</b> — layered networking for .NET </p>
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. 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. |
| .NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
| .NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen40 was computed. tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.0
- Microsoft.Bcl.AsyncInterfaces (>= 10.0.9)
- System.Buffers (>= 4.6.1)
- System.IO.Pipelines (>= 10.0.9)
NuGet packages (3)
Showing the top 3 NuGet packages that depend on PicoNode.Abs:
| Package | Downloads |
|---|---|
|
PicoNode.Http
HTTP protocol layer for PicoNode. Implements HTTP/1.1, HTTP/2 (including h2c upgrade), WebSocket (RFC 6455), and HPACK (RFC 7541) with Huffman encoding. Includes request/response serialization and routing. |
|
|
PicoNode.Web
Web application middleware framework for PicoNode. Provides WebApp builder, RadixTree router, middleware pipeline (CORS, compression, caching, security headers, static files), Server-Sent Events, and multipart form data parsing. |
|
|
PicoNode
TCP/UDP transport layer for PicoNode. Provides TcpNode and UdpNode with pipe-based I/O, backpressure, TLS/ALPN support, connection lifecycle management, and config hot-reload. |
GitHub repositories
This package is not used by any popular GitHub repositories.