Prepares IPC Connection

Adds simple IPC client and server and a service project. not fully implemented or tested yet.
This commit is contained in:
Serraniel 2023-07-24 23:28:45 +02:00
parent 8c4474c597
commit 00b30b2349
Signed by: Serraniel
GPG key ID: 3690B4E7364525D3
14 changed files with 284 additions and 1 deletions

View file

@ -5,7 +5,13 @@ VisualStudioVersion = 17.7.33913.275
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tagashi File Hub", "src\Tagashi File Hub\Tagashi File Hub.csproj", "{A4C5EF53-50CC-49D4-B750-C7FB2F848455}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TagashiFileHub.Core", "src\TagashiFileHub.Core\TagashiFileHub.Core.csproj", "{39536C05-9C87-44CA-A753-6A317F252666}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TagashiFileHub.Core", "src\TagashiFileHub.Core\TagashiFileHub.Core.csproj", "{39536C05-9C87-44CA-A753-6A317F252666}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TagashiFIleHub.Service", "src\TagashiFIleHub.Service\TagashiFIleHub.Service.csproj", "{368C0084-EBB4-4A9A-9E01-84C48CF184D9}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TagashiFileHub.Communication.Abstractions", "src\TagashiFileHub.Communication.Abstractions\TagashiFileHub.Communication.Abstractions.csproj", "{587B3D46-1114-4A84-8F33-4EEA501AE613}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TagashiFileHub.Communication.IPC", "src\TagashiFileHub.Communication.IPC\TagashiFileHub.Communication.IPC.csproj", "{64A274BB-678E-42C5-BD69-6BF7F04AD026}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -21,6 +27,18 @@ Global
{39536C05-9C87-44CA-A753-6A317F252666}.Debug|Any CPU.Build.0 = Debug|Any CPU
{39536C05-9C87-44CA-A753-6A317F252666}.Release|Any CPU.ActiveCfg = Release|Any CPU
{39536C05-9C87-44CA-A753-6A317F252666}.Release|Any CPU.Build.0 = Release|Any CPU
{368C0084-EBB4-4A9A-9E01-84C48CF184D9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{368C0084-EBB4-4A9A-9E01-84C48CF184D9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{368C0084-EBB4-4A9A-9E01-84C48CF184D9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{368C0084-EBB4-4A9A-9E01-84C48CF184D9}.Release|Any CPU.Build.0 = Release|Any CPU
{587B3D46-1114-4A84-8F33-4EEA501AE613}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{587B3D46-1114-4A84-8F33-4EEA501AE613}.Debug|Any CPU.Build.0 = Debug|Any CPU
{587B3D46-1114-4A84-8F33-4EEA501AE613}.Release|Any CPU.ActiveCfg = Release|Any CPU
{587B3D46-1114-4A84-8F33-4EEA501AE613}.Release|Any CPU.Build.0 = Release|Any CPU
{64A274BB-678E-42C5-BD69-6BF7F04AD026}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{64A274BB-678E-42C5-BD69-6BF7F04AD026}.Debug|Any CPU.Build.0 = Debug|Any CPU
{64A274BB-678E-42C5-BD69-6BF7F04AD026}.Release|Any CPU.ActiveCfg = Release|Any CPU
{64A274BB-678E-42C5-BD69-6BF7F04AD026}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View file

@ -0,0 +1,14 @@
using TagashiFileHub.Communication.IPC;
using TagashiFIleHub.Service;
var host = Host.CreateDefaultBuilder(args)
.UseWindowsService(config => { config.ServiceName = "Tagashi File Hub"; })
.ConfigureServices(services =>
{
services.AddSingleton<TagashiServer>();
services.AddHostedService<Worker>();
services.AddLogging();
})
.Build();
host.Run();

View file

@ -0,0 +1,11 @@
{
"profiles": {
"TagashiFIleHub.Service": {
"commandName": "Project",
"dotnetRunMessages": true,
"environmentVariables": {
"DOTNET_ENVIRONMENT": "Development"
}
}
}
}

View file

@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk.Worker">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<UserSecretsId>dotnet-TagashiFIleHub.Service-f42ced2e-207a-4058-b846-f77037e5ac5a</UserSecretsId>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="7.0.1" />
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="7.0.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\TagashiFileHub.Communication.IPC\TagashiFileHub.Communication.IPC.csproj" />
</ItemGroup>
</Project>

View file

@ -0,0 +1,28 @@
using TagashiFileHub.Communication.IPC;
namespace TagashiFIleHub.Service;
internal class Worker : BackgroundService
{
private readonly ILogger<Worker>? _logger;
private readonly TagashiServer _server;
public Worker(ILogger<Worker>? logger, TagashiServer server)
{
_logger = logger;
_server = server;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
_logger?.LogInformation("Starting service worker");
await _server.StartAsync("Service", stoppingToken);
while (!stoppingToken.IsCancellationRequested)
{
_logger?.LogInformation("Worker running at: {time}", DateTimeOffset.Now);
await Task.Delay(1000, stoppingToken);
}
}
}

View file

@ -0,0 +1,8 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.Hosting.Lifetime": "Information"
}
}
}

View file

@ -0,0 +1,8 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.Hosting.Lifetime": "Information"
}
}
}

View file

@ -0,0 +1,5 @@
namespace TagashiFileHub.Communication.Abstractions;
public interface ITagashiClient
{
}

View file

@ -0,0 +1,5 @@
namespace TagashiFileHub.Communication.Abstractions;
public interface ITagashiServer
{
}

View file

@ -0,0 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>

View file

@ -0,0 +1,11 @@
namespace TagashiFileHub.Communication.IPC;
internal static class IpcUtils
{
public static string PipeNamePrefix => "TagashiFileHub";
public static string PipeName(string name)
{
return $"{PipeNamePrefix}.{name}";
}
}

View file

@ -0,0 +1,63 @@
using System.IO.Pipes;
using Microsoft.Extensions.Logging;
using TagashiFileHub.Communication.Abstractions;
namespace TagashiFileHub.Communication.IPC;
public class TagashiClient : ITagashiClient, IDisposable
{
private readonly ILogger<TagashiClient>? _logger;
public TagashiClient(ILogger<TagashiClient>? logger)
{
_logger = logger;
}
private CancellationTokenSource DisconnectingSource { get; } = new();
private NamedPipeClientStream? ClientPipe { get; set; }
public void Dispose()
{
TryDisconnectCurrentPipe();
}
private void TryDisconnectCurrentPipe()
{
_logger?.LogInformation("Closing current pipe connection if existing.");
DisconnectingSource.Cancel(false);
ClientPipe?.Close();
ClientPipe?.Dispose();
ClientPipe = null;
}
public async Task ConnectAsync(string pipeName)
{
if (ClientPipe is not null)
{
_logger?.LogWarning("Client pipe already opened.");
TryDisconnectCurrentPipe();
}
_logger?.LogInformation($"Creating pipe for \".\" with name \"{pipeName}\"");
ClientPipe = new NamedPipeClientStream(".", IpcUtils.PipeName(pipeName), PipeDirection.InOut, PipeOptions.None);
await ClientPipe.ConnectAsync().ConfigureAwait(false);
DisconnectingSource.TryReset();
_ = NewListenTask(ClientPipe, DisconnectingSource.Token);
}
private static Task NewListenTask(NamedPipeClientStream clientPipe, CancellationToken cancellationToken)
{
return Task.Factory.StartNew(async () =>
{
while (!cancellationToken.IsCancellationRequested)
{
using var reader = new StreamReader(clientPipe);
var message = await reader.ReadLineAsync(cancellationToken).ConfigureAwait(false);
}
}, cancellationToken);
}
}

View file

@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="7.0.1" />
<PackageReference Include="System.IO.Pipes" Version="4.3.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\TagashiFileHub.Communication.Abstractions\TagashiFileHub.Communication.Abstractions.csproj" />
</ItemGroup>
</Project>

View file

@ -0,0 +1,66 @@
using System.Collections.Concurrent;
using System.IO.Pipes;
using Microsoft.Extensions.Logging;
using TagashiFileHub.Communication.Abstractions;
namespace TagashiFileHub.Communication.IPC;
public class TagashiServer : ITagashiServer
{
private readonly ConcurrentBag<StreamWriter> _clients = new();
private readonly ILogger<TagashiServer>? _logger;
public TagashiServer(ILogger<TagashiServer>? logger = null)
{
_logger = logger;
}
public async Task StartAsync(string pipeName, CancellationToken cancellationToken)
{
await Task.Run(async () =>
{
_logger?.LogInformation($"Starting up tagashi server for pipe \"{pipeName}\"");
while (!cancellationToken.IsCancellationRequested)
{
await using var serverPipe = new NamedPipeServerStream(IpcUtils.PipeName(pipeName), PipeDirection.InOut,
1, PipeTransmissionMode.Byte, PipeOptions.Asynchronous);
await serverPipe.WaitForConnectionAsync(cancellationToken);
using var reader = new StreamReader(serverPipe);
await using var writer = new StreamWriter(serverPipe) { AutoFlush = true };
_clients.Add(writer);
await HandleClientInternalAsync(reader, writer, cancellationToken).ConfigureAwait(false);
}
_logger?.LogInformation($"Shut down tagashi server for pipe \"{pipeName}\"");
}, cancellationToken).ConfigureAwait(false);
}
private async Task HandleClientInternalAsync(StreamReader reader, StreamWriter writer,
CancellationToken cancellationToken)
{
try
{
_logger?.LogInformation("New client connected");
while (!cancellationToken.IsCancellationRequested)
{
var message = await reader.ReadLineAsync(cancellationToken);
if (message == null)
break;
}
}
catch
{
// TODO
}
finally
{
_clients.TryTake(out writer!);
writer.Close();
_logger?.LogInformation("Client disconnected");
}
}
}