Yamux allows you to multiplex multiple logical streams over a single network connection, stream, or custom transport. This is useful for building efficient networked applications that require multiple independent data streams over a single connection.
using System.Net;
using System.Net.Sockets;
using Yamux;
var listener = new Socket(SocketType.Stream, ProtocolType.Tcp);
listener.Bind(new IPEndPoint(IPAddress.Loopback, 5000));
listener.Listen();
var clientSocket = await listener.AcceptAsync();
using var yamuxSession = new NetworkStream(clientSocket).AsYamuxSession(false);
yamuxSession.Start();
using System.Net;
using System.Net.Sockets;
using Yamux;
var socket = new Socket(SocketType.Stream, ProtocolType.Tcp);
await socket.ConnectAsync(new IPEndPoint(IPAddress.Loopback, 5000));
using var yamuxSession = new NetworkStream(socket).AsYamuxSession(true);
yamuxSession.Start();
Once you have a Yamux session established, you can create and manage multiple channels over the same connection.
// Opening a new channel (Client side)
var channel = await yamuxSession.OpenChannelAsync();
// Accepting incoming channels (Server side)
var channel = await yamuxSession.AcceptAsync();
You can read data from the channel using its Input pipe:
async Task HandleChannelAsync(ISessionChannel channel)
{
try
{
var reader = channel.Input;
while (true)
{
var result = await reader.ReadAsync();
var buffer = result.Buffer;
try
{
if (result.IsCanceled)
break;
// Process the data in the buffer
foreach (var segment in buffer)
{
// Work with the data...
}
if (result.IsCompleted)
break;
}
finally
{
reader.AdvanceTo(buffer.Start, buffer.End);
}
}
}
finally
{
channel.Close();
channel.Dispose();
}
}
await channel.WriteAsync(myData);
If you prefer working with streams, you can convert a channel to a Stream:
using var stream = channel.AsStream();
await stream.WriteAsync(buffer, 0, buffer.Length);
await stream.FlushAsync();
It is important to properly close and dispose of channels when you are done with them:
// Gracefully close the channel (write side)
channel.Close();
// Wait for the remote peer to acknowledge the close
await channel.WhenRemoteCloseAsync(TimeSpan.FromSeconds(5));
// Dispose the channel to release resources
channel.Dispose();
You can also use a using statement for automatic disposal:
using (var channel = await yamuxSession.OpenChannelAsync())
{
// Use the channel
channel.Close();
}
When opening a new channel, you can specify custom options:
var options = new SessionChannelOptions
{
ReceiveWindowSize = 256 * 1024, // 256KB window size
ReceiveWindowUpperBound = 4 * 1024 * 1024 // 4MB receive window upper bound
};
var channel = await yamuxSession.OpenChannelAsync(options);
Enable statistics on the session:
var sessionOptions = new SessionOptions
{
EnableStatistics = true
};
using var yamuxSession = new NetworkStream(socket).AsYamuxSession(true, sessionOptions);
yamuxSession.Start();
// Subscribe to session statistics
if (yamuxSession.Stats != null)
{
yamuxSession.Stats.Sampled += (sender, args) =>
{
Console.WriteLine($"Send rate: {yamuxSession.Stats.SendRate}/sec, Receive rate: {yamuxSession.Stats.ReceiveRate}/sec");
};
}