Загрузка данных


manager

using System;
using System.Diagnostics;
using System.IO;
using System.IO.MemoryMappedFiles;
using System.IO.Pipes;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

const string MemoryName = "PipeWorkerState";
const string MutexName = "PipeWorkerStateMutex";

string path = @"C:\Users\mompr\source\repos\PipeWorker\bin\Debug\net8.0\PipeWorker.exe";

if (!File.Exists(path))
{
    Console.WriteLine("Worker executable not found.");
    return;
}

using var memory = MemoryMappedFile.CreateOrOpen(MemoryName, 1024);
using var view = memory.CreateViewAccessor();
using var mutex = new Mutex(false, MutexName);

mutex.WaitOne();
try
{
    view.Write(0, 0);
    view.Write(4, false);
    view.Write(8, 0);
    WriteString(view, 12, "", 128);
    WriteString(view, 140, "", 256);
}
finally
{
    mutex.ReleaseMutex();
}

using var requestPipe = new AnonymousPipeServerStream(
    PipeDirection.Out, HandleInheritability.Inheritable);

using var responsePipe = new AnonymousPipeServerStream(
    PipeDirection.In, HandleInheritability.Inheritable);

var startInfo = new ProcessStartInfo(path)
{
    UseShellExecute = true,
    CreateNoWindow = false
};

startInfo.ArgumentList.Add(requestPipe.GetClientHandleAsString());
startInfo.ArgumentList.Add(responsePipe.GetClientHandleAsString());

using Process process = Process.Start(startInfo)!;

requestPipe.DisposeLocalCopyOfClientHandle();
responsePipe.DisposeLocalCopyOfClientHandle();

Console.WriteLine("Worker started.");
Console.WriteLine("Commands: stop, exit");

SendMessage(requestPipe, "TASK|Start|15");

Task<string?> inputTask = Task.Run(Console.ReadLine);

while (true)
{
    if (process.HasExited)
    {
        Console.WriteLine("Worker unavailable.");
        break;
    }

    if (inputTask.IsCompleted)
    {
        string command = (inputTask.Result ?? "").Trim();

        if (command.Equals("stop", StringComparison.OrdinalIgnoreCase))
        {
            Console.WriteLine("Stopping Worker...");

            if (!process.HasExited)
                process.Kill();

            process.WaitForExit();
            Console.WriteLine("Worker unavailable.");
            break;
        }

        if (command.Equals("exit", StringComparison.OrdinalIgnoreCase))
        {
            if (!process.HasExited)
                process.Kill();

            process.WaitForExit();
            break;
        }

        Console.WriteLine("Unknown command. Use stop or exit.");
        inputTask = Task.Run(Console.ReadLine);
    }

    WorkerState state = ReadState(view, mutex);

    Console.WriteLine(
        $"Task: {state.TaskId}, Progress: {state.Progress}%, " +
        $"Running: {state.IsRunning}, Status: {state.Status}, " +
        $"CurrentTask: {state.CurrentTask}");

    if (state.Progress == 100 && !state.IsRunning)
        break;

    Thread.Sleep(300);
}

if (!process.HasExited)
{
    try
    {
        string response = ReadMessage(responsePipe);
        Console.WriteLine($"Response: {response}");
    }
    catch (IOException)
    {
        Console.WriteLine("Worker unavailable.");
    }
}

static WorkerState ReadState(
    MemoryMappedViewAccessor view, Mutex mutex)
{
    mutex.WaitOne();
    try
    {
        return new WorkerState(
            view.ReadInt32(0),
            view.ReadBoolean(4),
            view.ReadInt32(8),
            ReadString(view, 12, 128),
            ReadString(view, 140, 256));
    }
    finally
    {
        mutex.ReleaseMutex();
    }
}

static void SendMessage(Stream pipe, string message)
{
    byte[] data = Encoding.UTF8.GetBytes(message);
    byte[] length = BitConverter.GetBytes(data.Length);

    pipe.Write(length, 0, length.Length);
    pipe.Write(data, 0, data.Length);
    pipe.Flush();
}

static string ReadMessage(Stream pipe)
{
    byte[] length = new byte[4];
    pipe.ReadExactly(length, 0, 4);

    int size = BitConverter.ToInt32(length, 0);

    if (size < 0 || size > 1024 * 1024)
        throw new InvalidDataException("Invalid message length.");

    byte[] data = new byte[size];
    pipe.ReadExactly(data, 0, size);

    return Encoding.UTF8.GetString(data);
}

static void WriteString(
    MemoryMappedViewAccessor view, int offset,
    string text, int maxBytes)
{
    byte[] data = Encoding.UTF8.GetBytes(text);
    int len = Math.Min(data.Length, maxBytes - 4);

    view.Write(offset, len);
    view.WriteArray(offset + 4, data, 0, len);
}

static string ReadString(
    MemoryMappedViewAccessor view, int offset, int maxBytes)
{
    int len = view.ReadInt32(offset);

    if (len < 0 || len > maxBytes - 4)
        return "";

    byte[] data = new byte[len];
    view.ReadArray(offset + 4, data, 0, len);

    return Encoding.UTF8.GetString(data);
}

class WorkerState
{
    public int Progress { get; }
    public bool IsRunning { get; }
    public int TaskId { get; }
    public string Status { get; }
    public string CurrentTask { get; }

    public WorkerState(int progress, bool isRunning, int taskId,
        string status, string currentTask)
    {
        Progress = progress;
        IsRunning = isRunning;
        TaskId = taskId;
        Status = status;
        CurrentTask = currentTask;
    }
}


worker

using System;
using System.IO;
using System.IO.MemoryMappedFiles;
using System.IO.Pipes;
using System.Text;
using System.Threading;

const string MemoryName = "PipeWorkerState";
const string MutexName = "PipeWorkerStateMutex";

if (args.Length < 2)
    return;

using var inputPipe = new AnonymousPipeClientStream(
    PipeDirection.In, args[0]);

using var outputPipe = new AnonymousPipeClientStream(
    PipeDirection.Out, args[1]);

using var memory = MemoryMappedFile.CreateOrOpen(MemoryName, 1024);
using var view = memory.CreateViewAccessor();
using var mutex = new Mutex(false, MutexName);

string message = ReadMessage(inputPipe);

if (!TryParseMessage(message, out string type,
    out string command, out string payload))
{
    SendMessage(outputPipe, "RESULT|Error: bad message format");
    return;
}

if (type != "TASK" || command != "Start")
{
    SendMessage(outputPipe, "RESULT|Error: unknown command");
    return;
}

int taskId = int.TryParse(payload, out int id) ? id : 15;

Console.WriteLine($"Task started: {taskId}");

for (int progress = 0; progress <= 100; progress += 10)
{
    UpdateState(view, mutex, progress, true, taskId,
        progress == 0 ? "Starting" : "Processing",
        "Processing data.csv");

    Thread.Sleep(500);
}

UpdateState(view, mutex, 100, false, taskId,
    "Finished", "None");

SendMessage(outputPipe, $"RESULT|Done task {payload}");

static bool TryParseMessage(string raw, out string type,
    out string command, out string payload)
{
    type = command = payload = "";

    string[] parts = raw.Split('|', 3);

    if (parts.Length != 3)
        return false;

    type = parts[0];
    command = parts[1];
    payload = parts[2];

    return true;
}

static void UpdateState(MemoryMappedViewAccessor view,
    Mutex mutex, int progress, bool running, int taskId,
    string status, string currentTask)
{
    mutex.WaitOne();
    try
    {
        view.Write(0, progress);
        view.Write(4, running);
        view.Write(8, taskId);

        WriteString(view, 12, status, 128);
        WriteString(view, 140, currentTask, 256);
    }
    finally
    {
        mutex.ReleaseMutex();
    }
}

static void SendMessage(Stream pipe, string message)
{
    byte[] data = Encoding.UTF8.GetBytes(message);
    byte[] length = BitConverter.GetBytes(data.Length);

    pipe.Write(length, 0, length.Length);
    pipe.Write(data, 0, data.Length);
    pipe.Flush();
}

static string ReadMessage(Stream pipe)
{
    byte[] length = new byte[4];
    pipe.ReadExactly(length, 0, 4);

    int size = BitConverter.ToInt32(length, 0);

    if (size < 0 || size > 1024 * 1024)
        throw new InvalidDataException("Invalid message length.");

    byte[] data = new byte[size];
    pipe.ReadExactly(data, 0, size);

    return Encoding.UTF8.GetString(data);
}

static void WriteString(MemoryMappedViewAccessor view,
    int offset, string text, int maxBytes)
{
    byte[] data = Encoding.UTF8.GetBytes(text);
    int len = Math.Min(data.Length, maxBytes - 4);

    view.Write(offset, len);
    view.WriteArray(offset + 4, data, 0, len);
}