NetConduit 1.0.1
See the version list below for details.
dotnet add package NetConduit --version 1.0.1
NuGet\Install-Package NetConduit -Version 1.0.1
<PackageReference Include="NetConduit" Version="1.0.1" />
<PackageVersion Include="NetConduit" Version="1.0.1" />
<PackageReference Include="NetConduit" />
paket add NetConduit --version 1.0.1
#r "nuget: NetConduit, 1.0.1"
#:package NetConduit@1.0.1
#addin nuget:?package=NetConduit&version=1.0.1
#tool nuget:?package=NetConduit&version=1.0.1
NetConduit
Transport-agnostic stream multiplexer for .NET. Creates multiple virtual channels over a single bidirectional stream.
N streams → 1 stream (mux) → N streams (demux)
Features
- Multiple channels over a single TCP/WebSocket/any stream connection
- Credit-based backpressure for flow control
- Priority queuing - higher priority frames sent first
- Auto-reconnection with channel state restoration
- Native AOT compatible - no reflection in core
- Modern .NET - targets .NET 8, 9, and 10
Installation
# Core package
dotnet add package NetConduit
# TCP transport helper
dotnet add package NetConduit.Tcp
# WebSocket transport helper
dotnet add package NetConduit.WebSocket
Quick Start
TCP Server
using NetConduit;
using NetConduit.Tcp;
// Accept a TCP connection
var connection = await TcpMultiplexer.AcceptAsync(listener);
var runTask = await connection.StartAsync();
// Accept channels from clients
await foreach (var channel in connection.Multiplexer.AcceptChannelsAsync())
{
// Each channel is a Stream - read data
var buffer = new byte[1024];
var bytesRead = await channel.ReadAsync(buffer);
Console.WriteLine($"Received on {channel.ChannelId}: {Encoding.UTF8.GetString(buffer, 0, bytesRead)}");
}
TCP Client
using NetConduit;
using NetConduit.Tcp;
// Connect to server
var connection = await TcpMultiplexer.ConnectAsync("localhost", 5000);
var runTask = await connection.StartAsync();
// Open a channel and send data
var channel = await connection.Multiplexer.OpenChannelAsync(
new ChannelOptions { ChannelId = "my-channel" });
await channel.WriteAsync(Encoding.UTF8.GetBytes("Hello, Server!"));
await channel.DisposeAsync(); // Sends FIN, closes channel gracefully
WebSocket
using NetConduit;
using NetConduit.WebSocket;
// Client
var connection = await WebSocketMultiplexer.ConnectAsync("ws://localhost:5000/ws");
var runTask = await connection.StartAsync();
// Server (ASP.NET Core)
app.MapGet("/ws", async (HttpContext context) =>
{
var webSocket = await context.WebSockets.AcceptWebSocketAsync();
var connection = WebSocketMultiplexer.Accept(webSocket);
var runTask = await connection.StartAsync();
// ...
});
Core Concepts
Channels
Channels are simplex (one-way) streams:
- WriteChannel: Opened by this side for sending data
- ReadChannel: Accepted from remote side for receiving data
For bidirectional communication, open two channels:
// Side A opens a channel - gets WriteChannel
var sendChannel = await muxA.OpenChannelAsync(new() { ChannelId = "A-to-B" });
// Side B accepts it - gets ReadChannel
var receiveChannel = await muxB.AcceptChannelAsync("A-to-B");
// For reverse direction, Side B opens another channel
var reverseSend = await muxB.OpenChannelAsync(new() { ChannelId = "B-to-A" });
var reverseReceive = await muxA.AcceptChannelAsync("B-to-A");
Raw Stream Usage
Channels inherit from Stream, so they work with any streaming API:
// Open a channel
var channel = await mux.OpenChannelAsync(new() { ChannelId = "data" });
// Use as Stream - works with StreamReader/Writer, CopyToAsync, etc.
using var writer = new StreamWriter(channel);
await writer.WriteLineAsync("Hello!");
await writer.FlushAsync();
Priority
Set channel priority at open time (0-255, higher = higher priority):
// Control messages - highest priority
var controlChannel = await mux.OpenChannelAsync(new()
{
ChannelId = "control",
Priority = ChannelPriority.Highest // 255
});
// Bulk data - lower priority
var dataChannel = await mux.OpenChannelAsync(new()
{
ChannelId = "bulk-data",
Priority = ChannelPriority.Low // 64
});
Backpressure
Credit-based flow control prevents fast senders from overwhelming slow receivers:
var options = new ChannelOptions
{
ChannelId = "controlled",
InitialCredits = 1024 * 1024, // 1MB buffer allowance
CreditGrantThreshold = 0.5, // Auto-grant when 50% consumed
SendTimeout = TimeSpan.FromSeconds(30) // Timeout if credits exhausted
};
var channel = await mux.OpenChannelAsync(options);
Transits
Transits add semantic meaning to channels:
MessageTransit - Send/receive JSON messages
using NetConduit.Transits;
using System.Text.Json.Serialization;
// Define message types with AOT-compatible serialization
public record ChatMessage(string User, string Text);
[JsonSerializable(typeof(ChatMessage))]
public partial class ChatContext : JsonSerializerContext { }
// Create transit
var transit = new MessageTransit<ChatMessage, ChatMessage>(
writeChannel, readChannel,
ChatContext.Default.ChatMessage,
ChatContext.Default.ChatMessage);
// Send/receive messages
await transit.SendAsync(new ChatMessage("Alice", "Hello!"));
var msg = await transit.ReceiveAsync();
Console.WriteLine($"{msg.User}: {msg.Text}");
DuplexStreamTransit - Bidirectional stream
// Wrap channel pair as bidirectional Stream
var duplex = new DuplexStreamTransit(writeChannel, readChannel);
// Use with any Stream API
await duplex.WriteAsync(data);
var bytesRead = await duplex.ReadAsync(buffer);
Reconnection
NetConduit supports automatic reconnection with channel state restoration:
var options = new MultiplexerOptions
{
EnableReconnection = true,
ReconnectTimeout = TimeSpan.FromSeconds(60),
ReconnectBufferSize = 1024 * 1024 // 1MB buffer for pending data
};
var mux = new StreamMultiplexer(stream, stream, options);
mux.OnDisconnected += () => Console.WriteLine("Disconnected, waiting for reconnect...");
mux.OnReconnected += () => Console.WriteLine("Reconnected!");
// After network disruption, reconnect with new streams
await mux.ReconnectAsync(newReadStream, newWriteStream);
Configuration
MultiplexerOptions
| Option | Default | Description |
|---|---|---|
MaxFrameSize |
16MB | Maximum payload per frame |
PingInterval |
30s | Heartbeat interval |
PingTimeout |
10s | Max wait for pong |
MaxMissedPings |
3 | Missed pings before disconnect |
EnableReconnection |
true | Enable reconnection support |
ReconnectTimeout |
60s | Max wait for reconnection |
FlushMode |
Batched | Frame flushing strategy |
FlushInterval |
1ms | Batched flush interval |
ChannelOptions
| Option | Default | Description |
|---|---|---|
ChannelId |
required | Unique channel identifier (0-1024 bytes UTF-8) |
InitialCredits |
1MB | Initial send buffer allowance |
CreditGrantThreshold |
0.5 | Auto-grant when X% consumed |
SendTimeout |
30s | Max wait for credits |
Priority |
Normal (128) | Channel priority (0-255) |
Statistics
// Multiplexer stats
var stats = mux.Stats;
Console.WriteLine($"Bytes sent: {stats.BytesSent}");
Console.WriteLine($"Open channels: {stats.OpenChannels}");
Console.WriteLine($"Last ping RTT: {stats.LastPingRtt}");
// Per-channel stats
var channelStats = channel.Stats;
Console.WriteLine($"Channel bytes: {channelStats.BytesSent}");
Samples
The repository includes complete sample applications:
| Sample | Description |
|---|---|
| ChatCli | CLI chat app with bidirectional messaging |
| FileTransfer | File transfer with progress and concurrent transfers |
| RpcFramework | Request/response RPC pattern |
| VideoStream | Simulated video/audio streaming with priority channels |
Run samples:
# Chat server
cd samples/NetConduit.Samples.ChatCli
dotnet run -- server 5000 Alice
# Chat client (another terminal)
dotnet run -- client 5000 localhost Bob
Architecture
┌──────────────────────────────────────────────────────────────┐
│ Application │
├──────────────────────────────────────────────────────────────┤
│ Transit Layer (Optional) │
│ ┌────────────────┐ ┌────────────────┐ ┌────────────────┐ │
│ │ MessageTransit │ │ StreamTransit │ │ DuplexStream │ │
│ └───────┬────────┘ └───────┬────────┘ └───────┬────────┘ │
├──────────┴───────────────────┴───────────────────┴───────────┤
│ NetConduit │
│ - Frame encoding/decoding (9-byte header) │
│ - Channel management (string ChannelId) │
│ - Credit-based backpressure │
│ - Priority queuing │
│ - Ping/pong heartbeat │
│ - GOAWAY graceful shutdown │
├──────────────────────────────────────────────────────────────┤
│ Transport Layer │
│ ┌────────────────┐ ┌────────────────┐ ┌────────────────┐ │
│ │ NetConduit.Tcp │ │ NetConduit.WS │ │ Any Stream │ │
│ └────────────────┘ └────────────────┘ └────────────────┘ │
└──────────────────────────────────────────────────────────────┘
Frame Format
┌─────────────────────────────────────────────────────────────┐
│ Channel Index (4B) │ Flags (1B) │ Length (4B) │ Payload │
└─────────────────────────────────────────────────────────────┘
- 9-byte header, big-endian encoding
- Max 16MB payload (configurable)
- Frame types: DATA, INIT, FIN, ACK, ERR
Performance
NetConduit uses several techniques for high performance:
System.IO.Pipelinesfor zero-copy readsArrayPool<byte>for buffer reuseChannel<T>for lock-free queuingstackallocfor header serializationBinaryPrimitivesfor fast encoding
Benchmarks
Raw TCP vs Multiplexed TCP Comparison
Compares N separate TCP connections (Raw TCP) vs 1 TCP connection with N multiplexed channels (Mux TCP):
| Channels | Data/Channel | Raw TCP | Mux TCP | Ratio | Notes |
|---|---|---|---|---|---|
| 1 | 100 KB | 60 ms | 71 ms | 1.18x | Similar performance |
| 1 | 1 MB | 65 ms | 99 ms | 1.52x | Similar performance |
| 10 | 1 KB | 65 ms | 82 ms | 1.27x | Similar performance |
| 10 | 100 KB | 65 ms | 82 ms | 1.25x | Similar performance |
| 10 | 1 MB | 66 ms | 70 ms | 1.06x | Nearly identical |
| 100 | 1 KB | 65 ms | 227 ms | 3.50x | Raw TCP faster |
| 100 | 100 KB | 64 ms | 315 ms | 4.90x | Raw TCP faster |
| 100 | 1 MB | 82 ms | 238 ms | 2.90x | Raw TCP faster |
| 1000 | 1 KB | 1,122 ms | 1,739 ms | 1.55x | Comparable |
| 1000 | 100 KB | 857 ms | 1,718 ms | 2.00x | Raw TCP faster |
| 1000 | 1 MB | N/A* | 1,398 ms | - | Mux works, Raw TCP fails |
*Raw TCP fails at 1000 connections × 1MB due to socket exhaustion
Key Insights:
- Low channel counts: Raw TCP and Mux perform similarly (1-10 channels)
- Resource efficiency: Mux uses 1 TCP connection vs N connections, reducing OS overhead
- Extreme concurrency: At 1000 channels with large data, Raw TCP hits OS limits while Mux succeeds
- Trade-off: Mux adds multiplexing overhead but provides channel isolation and reduces connection count
- Best use cases: Mux excels when you need many logical streams over limited connections (WebSocket, mobile, firewalls)
Running Benchmarks
cd benchmarks/NetConduit.Benchmarks
dotnet run -c Release
Available benchmark classes:
TcpVsMuxBenchmark- Direct Raw TCP vs Multiplexed comparisonThroughputBenchmark- Throughput with varying channel counts and data sizesScalabilityBenchmark- Scalability across different scenarios
License
MIT License - see LICENSE for details.
Contributing
Contributions welcome! Please read our contributing guidelines before submitting PRs.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. 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 is compatible. 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 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
- No dependencies.
-
net8.0
- No dependencies.
-
net9.0
- No dependencies.
NuGet packages (5)
Showing the top 5 NuGet packages that depend on NetConduit:
| Package | Downloads |
|---|---|
|
NetConduit.WebSocket
WebSocket transport helper for NetConduit stream multiplexer. Provides easy WebSocket client/server connection handling with automatic multiplexer setup. |
|
|
NetConduit.Tcp
TCP transport helper for NetConduit stream multiplexer. Provides easy TCP client/server connection handling with automatic multiplexer setup. |
|
|
NetConduit.Quic
QUIC transport helper for NetConduit stream multiplexer (System.Net.Quic). |
|
|
NetConduit.Ipc
Local IPC transport helper for NetConduit (named pipes on Windows, Unix domain sockets elsewhere). |
|
|
NetConduit.Udp
UDP transport helper for NetConduit stream multiplexer with a minimal reliable stream shim. |
GitHub repositories
This package is not used by any popular GitHub repositories.
## New Version
* Bump `net_conduit` from `0.1.0` to `1.0.1`. See [changelog](https://github.com/Kiryuumaru/NetConduit/compare/net_conduit/0.1.0...net_conduit/1.0.1)
## What's Changed
* Initial Release by @Kiryuumaru in https://github.com/Kiryuumaru/NetConduit/pull/6
* Add extension methods and enhance NetConduit usability by @Kiryuumaru in https://github.com/Kiryuumaru/NetConduit/pull/19
**Full Changelog**: https://github.com/Kiryuumaru/NetConduit/compare/build.7...build.20251204190256.4f8d5d6