Faactory.Channels.Abstractions
0.1.0-preview-9
See the version list below for details.
dotnet add package Faactory.Channels.Abstractions --version 0.1.0-preview-9
NuGet\Install-Package Faactory.Channels.Abstractions -Version 0.1.0-preview-9
<PackageReference Include="Faactory.Channels.Abstractions" Version="0.1.0-preview-9" />
paket add Faactory.Channels.Abstractions --version 0.1.0-preview-9
#r "nuget: Faactory.Channels.Abstractions, 0.1.0-preview-9"
// Install Faactory.Channels.Abstractions as a Cake Addin
#addin nuget:?package=Faactory.Channels.Abstractions&version=0.1.0-preview-9&prerelease
// Install Faactory.Channels.Abstractions as a Cake Tool
#tool nuget:?package=Faactory.Channels.Abstractions&version=0.1.0-preview-9&prerelease
Channels
A TCP communication library based on middleware components.
Design
The concept behind this library is to apply a middleware pipeline to data coming in and out from the open communication channels.
For data coming through the channel input, two middleware components can be applied: adapters and handlers.
graph LR;
channelInput((Input)) --> a1[/Adapter/]
subgraph adapters
a1 --> a2[/Adapter/]
end
a2 --> Handlers
For data going through the channel output, only adapters are applicable.
graph LR;
openChannel((Channel)) --> a1[/Adapter/]
subgraph adapters
a1 --> a2[/Adapter/]
end
a2 --> channelOutput([Output])
Adapters
An adapter is a middleware component that can be executed at any point in the pipeline and it has a single conceptual purpose: to adapt data.
graph LR;
in[/Data In/] --> Adapter --> out[/Data Out/]
An adapter is expected to forward data to next component in the pipeline, although that is not mandatory. However, if the adapter doesn't forward any data, the pipeline is interrupted and no other components will be executed.
Implementing an Adapter
Unless you have very specific needs, you should inherit your adapter from the ChannelAdapter<T>
abstract class and not implementing the IChannelAdapter
interface directly. This is because the base class does a few things for us that, if implementing the interface directly, won't be available. That includes
- Type checking
- Type mutation
Type checking is essentially making sure the type of the data is intended for an adapter. If it's not, data is automatically forwarded to the next middleware component in the pipeline.
Type mutation is the capacity to change the data type, if compatible with the expected data of the adapter. The base class already deals with IByteBuffer
←-> Byte[]
and T
←-> IEnumerable<T>
mutations, but it also provides an opportunity to override/extend this behaviour.
Here's an example of how to implement an adapter that adapts from an IByteBuffer
(or Byte[]
).
public class MyChannelAdapter : ChannelAdapter<IByteBuffer>
{
public override Task ExecuteAsync( IAdapterContext context, IByteBuffer data )
{
// adapt/transform data
var adaptedData = ...
// forward adapted data
context.Forward( adaptedData );
}
}
Ready-made Adapters
In addition to the abstract ChannelAdapter<T>
adapter, you have a few more ready-made adapters that you can use.
Adapter | Target | Description |
---|---|---|
AnonymousChannelAdapter | Input/Output | A quick way to implement an anonymous adapter |
BufferLengthAdapter | Input | Ensures the input buffer doesn't exceed in length |
Handlers
Although handlers are very similar to adapters, their conceptual purpose is different: to handle data. That means that business logic should be applied here and not on an adapter. Also, handlers are executed at the end of the pipeline and as such, they don't forward data.
graph LR;
in[/Data In/] --> Handler
Implementing an Handler
Similarly to the adapter, unless you have very specific needs, you should inherit your handler from the ChannelHandler<T>
class and not the IChannelHandler
interface directly. This is because, again, similarly to the adapter, the base class does a type checking for us; if the data type is not intended for the handler, then it (the handler) won't be executed.
Similarly to the adapter, the base class also deals with T
←-> IEnumerable<T>
mutations.
public class MyChannelHandler : ChannelHandler<MyData>
{
public override Task ExecuteAsync( IChannelContext context, MyData data )
{
// implement your handler here
}
}
Adapters vs Handlers
Because adapters and handlers are so similar, there might be a temptation to do everything with adapters. And while that's feasable, it's not recommended. Adapters should be used to adapt data and handlers to handle data (business logic).
- Adapters adapt and forward data
- Handlers handle data and business logic
- Adapters can run at any point in the pipeline
- Handlers run at the end of the pipeline
Writing to Output
At any point, within an adapter or handler, we can write data to the channel output; this will trigger the output pipeline and at the end, send the data to the other party. However, there are two distinct ways of doing so, with distinct behaviour.
1. Write directly to the Channel
This is the most straightforward method and it will immediately trigger the output pipeline, but it is not the recommended way, unless you need the data to reach the other party as soon as possible, no matter what happens next (current or next middleware component).
public override async Task ExecuteAsync( IAdapterContext context, IEnumerable<Message> data )
{
// ...
await context.Channel.WriteAsync( replyData );
}
2. Write to the Output buffer (recommended)
The context of an adapter or handler, gives us access to an output buffer that we can write to. This is the recommended method. Writing to the output buffer doesn't immediately trigger the output pipeline, instead, it is only triggered at the end of the pipeline, after all adapters and handlers have executed - without interruption. If an adapter interrupts the pipeline, or a handler crashes and interrupts the pipeline, the data in the buffer will never be written to the channel.
public override async Task ExecuteAsync( IAdapterContext context, IEnumerable<Message> data )
{
// ...
context.Output.Write( replyData );
}
Getting Started
Install the package from NuGet
dotnet add package Faactory.Channels --prerelease
To quickly bootstrap a server, we need an HostBuilder
to inject a hosted service. Then we need to configure the listening options and set up the input and output pipelines. Here's an example
var builder = new HostBuilder()
.ConfigureServices( ( context, services) =>
{
// add logging
services.AddLogging( loggingBuilder =>
{
loggingBuilder.AddConsole()
.SetMinimumLevel( LogLevel.Debug );
} );
// add our hosted service
services.AddChannelsHostedService( builder =>
{
// configure options
builder.Configure( options =>
{
options.Port = 8080;
options.Backlog = 30;
} );
// set up input pipeline
builder.AddInputAdapter<ExampleDecoderChannelAdapter>()
.AddInputHandler<MyChannelHandler>();
// set up output pipeline
builder.AddOutputAdapter<ExampleEncoderAdapter>();
} );
} )
.UseConsoleLifetime();
await builder.Build().RunAsync();
To boostrap the client, we'll need to register the factory with a service provider. Then, similarly to the server, we need to configure the channel options and set up the input and output pipelines. Here's an example
IServiceCollection services = ...
// add logging
services.AddLogging( loggingBuilder =>
{
loggingBuilder.AddConsole()
.SetMinimumLevel( LogLevel.Debug );
} );
// add our client factory
services.AddChannelsClient( builder =>
{
// configure options
builder.Configure( options =>
{
options.Host = "localhost";
options.Port = 8080;
} );
// set up input pipeline
builder.AddInputAdapter<ExampleDecoderChannelAdapter>()
.AddInputHandler<MyChannelHandler>();
// set up output pipeline
builder.AddOutputAdapter<ExampleEncoderAdapter>();
} );
var provider = services.BuildServiceProvider();
var channelFactory = provider.GetRequiredService<IClientChannelFactory>();
var channel = await channelFactory.CreateAsync();
await channel.WriteAsync( new MyData
{
// ...
} );
Adapters and Buffers
Although raw data handling in the adapters can be done with Byte[]
, it is recommended to use an IByteBuffer
instance instead, particularly for reading data. You can read more about it here.
Parcel Protocol
If you intend to use Parcel Protocol for communication, you have available an extension library. You can learn more about it here.
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net6.0 is compatible. 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. |
-
net6.0
- Faactory.Channels.Buffers (>= 0.1.0-preview-9)
- Faactory.Types (>= 1.5.1)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 6.0.0)
- Microsoft.Extensions.Logging.Abstractions (>= 6.0.1)
NuGet packages (1)
Showing the top 1 NuGet packages that depend on Faactory.Channels.Abstractions:
Package | Downloads |
---|---|
Faactory.Channels.Ruptela
Channels - Ruptela Protocol |
GitHub repositories
This package is not used by any popular GitHub repositories.
Version | Downloads | Last updated | |
---|---|---|---|
0.7.0-preview-3 | 153 | 2/7/2023 | |
0.7.0-preview-2 | 134 | 2/7/2023 | |
0.7.0-preview-1 | 151 | 2/7/2023 | |
0.6.1 | 371 | 1/27/2023 | |
0.6.0 | 341 | 1/27/2023 | |
0.5.2 | 385 | 1/26/2023 | |
0.5.1 | 705 | 1/24/2023 | |
0.5.0 | 666 | 1/6/2023 | |
0.5.0-preview-4 | 155 | 1/5/2023 | |
0.5.0-preview-3 | 159 | 1/4/2023 | |
0.5.0-preview-2 | 164 | 1/4/2023 | |
0.5.0-preview-1 | 178 | 12/23/2022 | |
0.4.0 | 875 | 11/21/2022 | |
0.3.2 | 565 | 11/7/2022 | |
0.3.1 | 794 | 7/14/2022 | |
0.3.0 | 1,050 | 7/4/2022 | |
0.2.0 | 544 | 6/10/2022 | |
0.2.0-preview-1 | 167 | 6/10/2022 | |
0.1.1 | 826 | 4/12/2022 | |
0.1.0 | 1,017 | 4/12/2022 | |
0.1.0-preview-9 | 190 | 4/8/2022 | |
0.1.0-preview-11 | 421 | 4/12/2022 | |
0.1.0-preview-10 | 207 | 4/11/2022 |