Prepares IPC Connection
Adds simple IPC client and server and a service project. not fully implemented or tested yet.
This commit is contained in:
parent
8c4474c597
commit
00b30b2349
|
@ -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
|
||||
|
|
14
src/TagashiFIleHub.Service/Program.cs
Normal file
14
src/TagashiFIleHub.Service/Program.cs
Normal 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();
|
11
src/TagashiFIleHub.Service/Properties/launchSettings.json
Normal file
11
src/TagashiFIleHub.Service/Properties/launchSettings.json
Normal file
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"profiles": {
|
||||
"TagashiFIleHub.Service": {
|
||||
"commandName": "Project",
|
||||
"dotnetRunMessages": true,
|
||||
"environmentVariables": {
|
||||
"DOTNET_ENVIRONMENT": "Development"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
19
src/TagashiFIleHub.Service/TagashiFIleHub.Service.csproj
Normal file
19
src/TagashiFIleHub.Service/TagashiFIleHub.Service.csproj
Normal 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>
|
28
src/TagashiFIleHub.Service/Worker.cs
Normal file
28
src/TagashiFIleHub.Service/Worker.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
8
src/TagashiFIleHub.Service/appsettings.Development.json
Normal file
8
src/TagashiFIleHub.Service/appsettings.Development.json
Normal file
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.Hosting.Lifetime": "Information"
|
||||
}
|
||||
}
|
||||
}
|
8
src/TagashiFIleHub.Service/appsettings.json
Normal file
8
src/TagashiFIleHub.Service/appsettings.json
Normal file
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.Hosting.Lifetime": "Information"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
namespace TagashiFileHub.Communication.Abstractions;
|
||||
|
||||
public interface ITagashiClient
|
||||
{
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
namespace TagashiFileHub.Communication.Abstractions;
|
||||
|
||||
public interface ITagashiServer
|
||||
{
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
11
src/TagashiFileHub.Communication.IPC/IpcUtils.cs
Normal file
11
src/TagashiFileHub.Communication.IPC/IpcUtils.cs
Normal 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}";
|
||||
}
|
||||
}
|
63
src/TagashiFileHub.Communication.IPC/TagashiClient.cs
Normal file
63
src/TagashiFileHub.Communication.IPC/TagashiClient.cs
Normal 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);
|
||||
}
|
||||
}
|
|
@ -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>
|
66
src/TagashiFileHub.Communication.IPC/TagashiServer.cs
Normal file
66
src/TagashiFileHub.Communication.IPC/TagashiServer.cs
Normal 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");
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue