NetConduit 1.0.1

This package has a SemVer 2.0.0 package version: 1.0.1+build.20251204190256.4f8d5d6.
There is a newer version of this package available.
See the version list below for details.
dotnet add package NetConduit --version 1.0.1
                    
NuGet\Install-Package NetConduit -Version 1.0.1
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="NetConduit" Version="1.0.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="NetConduit" Version="1.0.1" />
                    
Directory.Packages.props
<PackageReference Include="NetConduit" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add NetConduit --version 1.0.1
                    
#r "nuget: NetConduit, 1.0.1"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package NetConduit@1.0.1
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=NetConduit&version=1.0.1
                    
Install as a Cake Addin
#tool nuget:?package=NetConduit&version=1.0.1
                    
Install as a Cake Tool

NetConduit

NuGet License: MIT

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.Pipelines for zero-copy reads
  • ArrayPool<byte> for buffer reuse
  • Channel<T> for lock-free queuing
  • stackalloc for header serialization
  • BinaryPrimitives for 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 comparison
  • ThroughputBenchmark - Throughput with varying channel counts and data sizes
  • ScalabilityBenchmark - Scalability across different scenarios

License

MIT License - see LICENSE for details.

Contributing

Contributions welcome! Please read our contributing guidelines before submitting PRs.

Product 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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • 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.

Version Downloads Last Updated
1.2.6 2,998 12/6/2025
1.2.4 166 12/6/2025
1.2.3 147 12/5/2025
1.2.2 93 12/5/2025
1.2.1 87 12/5/2025
1.1.3 273 12/5/2025
1.1.2 27 12/4/2025
1.0.1 752 12/4/2025

## 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