Channel interaction module refresh base

This commit is contained in:
2025-04-30 12:25:23 +03:00
parent 3d3772a341
commit d2a41150f1
12 changed files with 143 additions and 90 deletions
+1 -1
View File
@@ -19,7 +19,7 @@ class Program
builder.Modules.Add(new BackendIdentityGenerator()); builder.Modules.Add(new BackendIdentityGenerator());
// builder.UseNetworkGateway(new IPEndPoint(IPAddress.Loopback, 4567), typeof(NextGenerationInteractionModule), // builder.UseNetworkGateway(new IPEndPoint(IPAddress.Loopback, 4567), typeof(NextGenerationInteractionModule),
// builder.GetModule<IIdentityGenerator>()!); // builder.GetModule<IIdentityGenerator>()!);
builder.UseNetworkGateway(new IPEndPoint(IPAddress.Loopback, 4567), typeof(NextGenerationInteractionModule), builder.UseNetworkGateway(new IPEndPoint(IPAddress.Loopback, 4567), typeof(ChannelInteractionModule),
builder.GetModule<IIdentityGenerator>()!); builder.GetModule<IIdentityGenerator>()!);
builder.Modules.Add(new ConnectionHub()); builder.Modules.Add(new ConnectionHub());
+2 -2
View File
@@ -23,7 +23,7 @@ class Program
builder.Modules.Add(new CborSerializationToolkit()); builder.Modules.Add(new CborSerializationToolkit());
builder.Modules.Add(new RemoteContextRepository()); builder.Modules.Add(new RemoteContextRepository());
builder.Modules.Add(new NextGenerationInteractionModule()); builder.Modules.Add(new ChannelInteractionModule());
builder.Modules.Add(new RepresentationModule()); builder.Modules.Add(new RepresentationModule());
builder.Modules.Add(new NetworkFrontendBridge(new IPEndPoint(IPAddress.Loopback, 4567))); builder.Modules.Add(new NetworkFrontendBridge(new IPEndPoint(IPAddress.Loopback, 4567)));
builder.Modules.Add(new StaticRepresentationModuleProducer()); builder.Modules.Add(new StaticRepresentationModuleProducer());
@@ -58,7 +58,7 @@ class Program
Console.WriteLine("Printer created"); Console.WriteLine("Printer created");
Thread.Sleep(100); Thread.Sleep(100);
frontendBridge.Obstacle(); // frontendBridge.Obstacle();
var name = disposingPrinter.GetName(); var name = disposingPrinter.GetName();
DemoCheck.BasicNonParamsCall = true; DemoCheck.BasicNonParamsCall = true;
Console.WriteLine("Printer name : {0}", name); Console.WriteLine("Printer name : {0}", name);
+4 -4
View File
@@ -11,17 +11,17 @@ namespace mROA.Test
public class NextGenTest public class NextGenTest
{ {
private TcpListener _listener; private TcpListener _listener;
private NextGenerationInteractionModule _interactionModuleA; private ChannelInteractionModule _interactionModuleA;
private NextGenerationInteractionModule _interactionModuleB; private ChannelInteractionModule _interactionModuleB;
private Guid[] guids = [Guid.NewGuid(), Guid.NewGuid(), Guid.NewGuid()]; private Guid[] guids = [Guid.NewGuid(), Guid.NewGuid(), Guid.NewGuid()];
[SetUp] [SetUp]
public void Setup() public void Setup()
{ {
_listener = new TcpListener(IPAddress.Loopback, 4567); _listener = new TcpListener(IPAddress.Loopback, 4567);
_interactionModuleA = new NextGenerationInteractionModule(); _interactionModuleA = new ChannelInteractionModule();
_interactionModuleA.Inject(new JsonSerializationToolkit()); _interactionModuleA.Inject(new JsonSerializationToolkit());
_interactionModuleB = new NextGenerationInteractionModule(); _interactionModuleB = new ChannelInteractionModule();
_interactionModuleB.Inject(new JsonSerializationToolkit()); _interactionModuleB.Inject(new JsonSerializationToolkit());
} }
+2 -2
View File
@@ -6,8 +6,8 @@
public interface IConnectionHub : IInjectableModule public interface IConnectionHub : IInjectableModule
{ {
void RegisterInteraction(INextGenerationInteractionModule interaction); void RegisterInteraction(IChannelInteractionModule interaction);
INextGenerationInteractionModule GetInteraction(int id); IChannelInteractionModule GetInteraction(int id);
event ConnectionHandler? OnConnected; event ConnectionHandler? OnConnected;
event DisconnectionHandler? OnDisconnected; event DisconnectionHandler? OnDisconnected;
} }
+5 -6
View File
@@ -6,18 +6,17 @@ using mROA.Implementation;
namespace mROA.Abstract namespace mROA.Abstract
{ {
public interface INextGenerationInteractionModule : IInjectableModule, IDisposable public interface IChannelInteractionModule : IInjectableModule, IDisposable
{ {
int ConnectionId { get; set; } int ConnectionId { get; set; }
Stream? BaseStream { get; set; } ChannelWriter<NetworkMessageHeader> ReceiveChanel { get; }
ChannelReader<NetworkMessageHeader> UntrustedReceiveChanel { get; set; } ChannelReader<NetworkMessageHeader> TrustedPostChanel { get; }
ChannelWriter<(int clientId, NetworkMessageHeader messageHeader)> UntrustedPostChanel { get; set; } ChannelReader<NetworkMessageHeader> UntrustedPostChanel { get; }
Action<bool> IsConnected { get; set; }
Task<NetworkMessageHeader> GetNextMessageReceiving(bool infinite = true); Task<NetworkMessageHeader> GetNextMessageReceiving(bool infinite = true);
Task PostMessageAsync(NetworkMessageHeader messageHeader); Task PostMessageAsync(NetworkMessageHeader messageHeader);
Task PostMessageUntrustedAsync(NetworkMessageHeader messageHeader); Task PostMessageUntrustedAsync(NetworkMessageHeader messageHeader);
void HandleMessage(NetworkMessageHeader messageHeader); void HandleMessage(NetworkMessageHeader messageHeader);
// NetworkMessageHeader[] UnhandledMessages { get; }
NetworkMessageHeader? FirstByFilter(Predicate<NetworkMessageHeader> predicate); NetworkMessageHeader? FirstByFilter(Predicate<NetworkMessageHeader> predicate);
event Action<int> OnDisconnected; event Action<int> OnDisconnected;
Task Restart(bool sendRecovery); Task Restart(bool sendRecovery);
+3 -3
View File
@@ -6,10 +6,10 @@ namespace mROA.Implementation.Backend
{ {
public class ConnectionHub : IConnectionHub public class ConnectionHub : IConnectionHub
{ {
private readonly Dictionary<int, INextGenerationInteractionModule> _connections = new(); private readonly Dictionary<int, IChannelInteractionModule> _connections = new();
private ISerializationToolkit? _serializationToolkit; private ISerializationToolkit? _serializationToolkit;
public void RegisterInteraction(INextGenerationInteractionModule interaction) public void RegisterInteraction(IChannelInteractionModule interaction)
{ {
if (_serializationToolkit is null) if (_serializationToolkit is null)
throw new NullReferenceException("Serialization toolkit is null"); throw new NullReferenceException("Serialization toolkit is null");
@@ -21,7 +21,7 @@ namespace mROA.Implementation.Backend
OnConnected?.Invoke(module); OnConnected?.Invoke(module);
} }
public INextGenerationInteractionModule GetInteraction(int id) public IChannelInteractionModule GetInteraction(int id)
{ {
return _connections!.GetValueOrDefault(id, null) ?? throw new Exception("No connection found"); return _connections!.GetValueOrDefault(id, null) ?? throw new Exception("No connection found");
} }
@@ -67,19 +67,21 @@ namespace mROA.Implementation.Backend
{ {
var client = _tcpListener.AcceptTcpClient(); var client = _tcpListener.AcceptTcpClient();
Console.WriteLine($"Client connected from {client.Client.RemoteEndPoint}"); Console.WriteLine($"Client connected from {client.Client.RemoteEndPoint}");
var interaction = Activator.CreateInstance(_interactionModuleType!) as INextGenerationInteractionModule; var interaction = Activator.CreateInstance(_interactionModuleType!) as IChannelInteractionModule;
foreach (var injectableModule in _injectableModules!) foreach (var injectableModule in _injectableModules!)
interaction!.Inject(injectableModule); interaction!.Inject(injectableModule);
interaction!.Inject(_serialization); interaction!.Inject(_serialization);
interaction.BaseStream = client.GetStream(); interaction.BaseStream = client.GetStream();
interaction.UntrustedReceiveChanel = Channel.CreateUnbounded<NetworkMessageHeader>(new UnboundedChannelOptions var channel = Channel.CreateUnbounded<NetworkMessageHeader>(new UnboundedChannelOptions
{ {
SingleWriter = false, SingleWriter = false,
SingleReader = false, SingleReader = false,
AllowSynchronousContinuations = true AllowSynchronousContinuations = true
}).Reader; });
interaction.UntrustedReceiveChanel = channel.Reader;
interaction.UntrustedReceiveChanelWriter = channel.Writer;
var connectionRequest = interaction.GetNextMessageReceiving(false) var connectionRequest = interaction.GetNextMessageReceiving(false)
.GetAwaiter().GetResult()!; .GetAwaiter().GetResult()!;
@@ -2,43 +2,59 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Threading;
using System.Threading.Channels; using System.Threading.Channels;
using System.Threading.Tasks; using System.Threading.Tasks;
using mROA.Abstract; using mROA.Abstract;
namespace mROA.Implementation namespace mROA.Implementation
{ {
public class NextGenerationInteractionModule : INextGenerationInteractionModule public class ChannelInteractionModule : IChannelInteractionModule
{ {
private int DebugId = new Random().Next(); private readonly ChannelReader<NetworkMessageHeader> _receiveReader;
private readonly Channel<NetworkMessageHeader> _inputChannel;
private readonly Channel<NetworkMessageHeader> _outputTrustedChannel;
private readonly Channel<NetworkMessageHeader> _outputUntrustedChannel;
private const int BufferSize = ushort.MaxValue; private const int BufferSize = ushort.MaxValue;
private readonly Memory<byte> _buffer = new byte[BufferSize]; private readonly Memory<byte> _buffer = new byte[BufferSize];
private readonly List<NetworkMessageHeader> _messageBuffer = new(128); private readonly List<NetworkMessageHeader> _messageBuffer = new(128);
private Task<NetworkMessageHeader>? _currentReceiving; private Task<NetworkMessageHeader>? _currentReceiving;
private ISerializationToolkit? _serialization; private ISerializationToolkit? _serialization;
private Stream? _baseStream;
private bool _isConnected = true; private bool _isConnected = true;
private bool _isInReconnectionState; private bool _isInReconnectionState;
private bool _isActive = true; private bool _isActive = true;
private TaskCompletionSource<Stream> _reconnection; private TaskCompletionSource<Stream> _reconnection;
private ValueTask<NetworkMessageHeader>? _trustedReceive;
private TaskCompletionSource<NetworkMessageHeader> _untrustedReceive;
public NextGenerationInteractionModule() public ChannelInteractionModule()
{ {
_reconnection = new TaskCompletionSource<Stream>(); _reconnection = new TaskCompletionSource<Stream>();
_inputChannel = Channel.CreateUnbounded<NetworkMessageHeader>(new UnboundedChannelOptions
{
SingleReader = false,
SingleWriter = false,
AllowSynchronousContinuations = true
});
_receiveReader = _inputChannel.Reader;
_outputTrustedChannel = Channel.CreateUnbounded<NetworkMessageHeader>(new UnboundedChannelOptions
{
SingleReader = true,
SingleWriter = true,
AllowSynchronousContinuations = true
});
_outputUntrustedChannel = Channel.CreateUnbounded<NetworkMessageHeader>(new UnboundedChannelOptions
{
SingleReader = true,
SingleWriter = true,
AllowSynchronousContinuations = true
});
} }
public int ConnectionId { get; set; } public int ConnectionId { get; set; }
public Stream? BaseStream public ChannelWriter<NetworkMessageHeader> ReceiveChanel => _inputChannel.Writer;
{ public ChannelReader<NetworkMessageHeader> TrustedPostChanel => _outputTrustedChannel.Reader;
get => _baseStream; public ChannelReader<NetworkMessageHeader> UntrustedPostChanel => _outputUntrustedChannel.Reader;
set => _baseStream = value; public Action<bool> IsConnected { get; set; }
}
public ChannelReader<NetworkMessageHeader> UntrustedReceiveChanel { get; set; }
public ChannelWriter<(int clientId, NetworkMessageHeader messageHeader)> UntrustedPostChanel { get; set; }
public void Inject<T>(T dependency) public void Inject<T>(T dependency)
@@ -152,46 +168,10 @@ namespace mROA.Implementation
try try
{ {
NetworkMessageHeader message; var message = await _receiveReader.ReadAsync();
var wasNull = _untrustedReceive is null;
_trustedReceive ??= Receive();
_untrustedReceive = new TaskCompletionSource<NetworkMessageHeader>();
if (!wasNull)
{
if (_trustedReceive.Value.IsCompleted)
{
_trustedReceive = Receive();
}
if (_untrustedReceive.Task.IsCompleted)
{
_untrustedReceive = new TaskCompletionSource<NetworkMessageHeader>();
_ = UntrustedReceiveChanel.ReadAsync().AsTask()
.ContinueWith(task => _untrustedReceive.SetResult(task.Result));
}
}
else
{
_ = UntrustedReceiveChanel.ReadAsync().AsTask()
.ContinueWith(task => _untrustedReceive.SetResult(task.Result));
}
await Task.WhenAny(_trustedReceive.Value.AsTask() , _untrustedReceive.Task);
message = _trustedReceive.Value.IsCompleted
? _trustedReceive.Value.Result
: _untrustedReceive.Task.Result;
_currentReceiving = Task.Run(async () => await GetNextMessage());
return message; return message;
} }
catch (Exception ex) catch (Exception)
{ {
if (!_isActive) if (!_isActive)
{ {
@@ -276,12 +256,6 @@ namespace mROA.Implementation
lock (_reconnection) lock (_reconnection)
{ {
Console.WriteLine("Got lock from {0}", source); Console.WriteLine("Got lock from {0}", source);
if (_isConnected || _isInReconnectionState)
{
Console.WriteLine(
$"{source} {_isConnected} {_isInReconnectionState} {!_baseStream.CanRead} {!_baseStream.CanWrite}");
return;
}
Console.WriteLine("Call OnDisconnected from {0}", source); Console.WriteLine("Call OnDisconnected from {0}", source);
_isInReconnectionState = true; _isInReconnectionState = true;
@@ -310,8 +284,6 @@ namespace mROA.Implementation
{ {
_currentReceiving?.Dispose(); _currentReceiving?.Dispose();
} }
_baseStream?.Dispose();
} }
} }
} }
@@ -12,7 +12,7 @@ namespace mROA.Implementation.Frontend
{ {
private readonly IPEndPoint _serverEndPoint; private readonly IPEndPoint _serverEndPoint;
private TcpClient _tcpClient = new(); private TcpClient _tcpClient = new();
private INextGenerationInteractionModule? _interactionModule; private IChannelInteractionModule? _interactionModule;
private ISerializationToolkit? _serialization; private ISerializationToolkit? _serialization;
public NetworkFrontendBridge(IPEndPoint serverEndPoint) public NetworkFrontendBridge(IPEndPoint serverEndPoint)
@@ -24,7 +24,7 @@ namespace mROA.Implementation.Frontend
{ {
switch (dependency) switch (dependency)
{ {
case NextGenerationInteractionModule interactionModule: case ChannelInteractionModule interactionModule:
_interactionModule = interactionModule; _interactionModule = interactionModule;
break; break;
case ISerializationToolkit toolkit: case ISerializationToolkit toolkit:
@@ -43,12 +43,14 @@ namespace mROA.Implementation.Frontend
_tcpClient.Connect(_serverEndPoint); _tcpClient.Connect(_serverEndPoint);
_interactionModule.BaseStream = _tcpClient.GetStream(); _interactionModule.BaseStream = _tcpClient.GetStream();
_interactionModule.UntrustedReceiveChanel = Channel.CreateUnbounded<NetworkMessageHeader>(new UnboundedChannelOptions var channel = Channel.CreateUnbounded<NetworkMessageHeader>(new UnboundedChannelOptions
{ {
SingleWriter = false, SingleWriter = false,
SingleReader = false, SingleReader = false,
AllowSynchronousContinuations = true AllowSynchronousContinuations = true
}).Reader; });
_interactionModule.UntrustedReceiveChanel = channel.Reader;
_interactionModule.UntrustedReceiveChanelWriter = channel.Writer;
_interactionModule.OnDisconnected += id => { Reconnect(); }; _interactionModule.OnDisconnected += id => { Reconnect(); };
_interactionModule.PostMessageAsync(new NetworkMessageHeader(_serialization, new ClientConnect())).Wait(); _interactionModule.PostMessageAsync(new NetworkMessageHeader(_serialization, new ClientConnect())).Wait();
@@ -70,12 +72,14 @@ namespace mROA.Implementation.Frontend
_tcpClient = new TcpClient(); _tcpClient = new TcpClient();
_tcpClient.Connect(_serverEndPoint); _tcpClient.Connect(_serverEndPoint);
_interactionModule.BaseStream = _tcpClient.GetStream(); _interactionModule.BaseStream = _tcpClient.GetStream();
_interactionModule.UntrustedReceiveChanel = Channel.CreateUnbounded<NetworkMessageHeader>(new UnboundedChannelOptions var channel = Channel.CreateUnbounded<NetworkMessageHeader>(new UnboundedChannelOptions
{ {
SingleWriter = false, SingleWriter = false,
SingleReader = false, SingleReader = false,
AllowSynchronousContinuations = true AllowSynchronousContinuations = true
}).Reader; });
_interactionModule.UntrustedReceiveChanel = channel.Reader;
_interactionModule.UntrustedReceiveChanelWriter = channel.Writer;
await _interactionModule.Restart(true); await _interactionModule.Restart(true);
} }
+2 -2
View File
@@ -7,7 +7,7 @@ namespace mROA.Implementation
{ {
public class RepresentationModule : IRepresentationModule public class RepresentationModule : IRepresentationModule
{ {
private INextGenerationInteractionModule? _interaction; private IChannelInteractionModule? _interaction;
private ISerializationToolkit? _serialization; private ISerializationToolkit? _serialization;
public void Inject<T>(T dependency) public void Inject<T>(T dependency)
@@ -17,7 +17,7 @@ namespace mROA.Implementation
case ISerializationToolkit toolkit: case ISerializationToolkit toolkit:
_serialization = toolkit; _serialization = toolkit;
break; break;
case INextGenerationInteractionModule interactionModule: case IChannelInteractionModule interactionModule:
_interaction = interactionModule; _interaction = interactionModule;
break; break;
} }
+76
View File
@@ -0,0 +1,76 @@
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using mROA.Abstract;
namespace mROA.Implementation
{
public class StreamExtractor
{
private readonly Stream _ioStream;
private readonly ISerializationToolkit _serializationToolkit;
private const int BufferSize = ushort.MaxValue;
private readonly Memory<byte> _buffer = new byte[BufferSize];
private bool _manualConnectionState = true;
public StreamExtractor(Stream ioStream, ISerializationToolkit serializationToolkit)
{
_ioStream = ioStream;
_serializationToolkit = serializationToolkit;
}
public event Action<NetworkMessageHeader> MessageReceived;
private ushort ReadMessageLength()
{
var firstBit = _ioStream.ReadByte();
if (firstBit == -1)
{
_manualConnectionState = false;
throw new EndOfStreamException();
}
_manualConnectionState = true;
var secondBit = (byte)_ioStream.ReadByte();
var len = BitConverter.ToUInt16(new[] { (byte)firstBit, secondBit });
return len;
}
public async Task SingleReceive()
{
var len = ReadMessageLength();
var localSpan = _buffer[..len];
await _ioStream.ReadExactlyAsync(localSpan);
var message = _serializationToolkit.Deserialize<NetworkMessageHeader>(localSpan.Span);
#if TRACE
Console.WriteLine($"{DateTime.Now.TimeOfDay} Received Message {message.Id} - {message.MessageType}");
TransmissionConfig.TotalTransmittedBytes += len;
Console.WriteLine($"Total received bytes are {TransmissionConfig.TotalTransmittedBytes}");
#endif
MessageReceived(message);
}
public async Task InfiniteReceive(CancellationToken token)
{
while (token.IsCancellationRequested == false)
{
await SingleReceive();
}
}
public async Task Send(NetworkMessageHeader message)
{
var rawMessage = _serializationToolkit.Serialize(message);
var header = BitConverter.GetBytes((ushort)rawMessage.Length).AsMemory(0, sizeof(ushort));
await _ioStream.WriteAsync(header);
await _ioStream.WriteAsync(rawMessage);
}
public bool IsConnected => _ioStream is { CanRead: true, CanWrite: true } && _manualConnectionState;
}
}
+1 -1
View File
@@ -21,7 +21,7 @@
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' "> <PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
<DefineConstants></DefineConstants> <DefineConstants>TRACE;</DefineConstants>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>