Reputation: 11
Got this standard library I'm developing for standardized inter-process comms using memory mapped files (mmfs). It appears to properly create the mmf on the server side, but the client doesn't seem to be able to see it. (FileNotFound error on OpenExisting). (And yes I'm inputting the same name on both ends) Currently operating in a windows environment (Win 11). I am new to operating with MMFs so my apologies if this is just a silly mistake. I'd rather not use PInvoke as I plan on having a linux side and don't particularly want to deal with crossplatform native compatibility. The processes are running on the same computer. (And one isn't a child process of another, they're effectively just completely different processes on the same computer). Code below:
// Standard Library containing the Server and Client classes.
[SupportedOSPlatform("windows")]
public class Client
{
public string ServerName { get; private set; }
public bool Ready { get; private set; } = false;
internal static string Global => Server.Global;
private readonly Mutex mutex;
private readonly EventWaitHandle commandEvent, responseEvent, processingInProgress;
private static bool VerifyServer(string name)
{
if (OperatingSystem.IsWindows())
try
{
_ = MemoryMappedFile.OpenExisting(name);
return true;
}
catch (FileNotFoundException)
{
return false;
}
catch
{
return false;
}
return false;
}
public Client(string serverName)
{
ServerName = Global + serverName.MakeAlphanumeral();
Ready = VerifyServer(serverName);
if (!Ready)
{
Console.WriteLine($"There is no open server with name {serverName}, this client is now obsolete.");
return;
}
string mutexName = Global + serverName + "_MMFmutex";
string commandEventName = Global + serverName + "_CommandEvent";
string responseEventName = Global + serverName + "_ResponseEvent";
string processingInProgressName = Global + serverName + "_ProcessingInProgress";
bool s = Mutex.TryOpenExisting(mutexName, out mutex);
new InvalidOperationException($"Failed to obtain server mutex.").ThrowIfNot(s);
s = EventWaitHandle.TryOpenExisting(commandEventName, out commandEvent);
new InvalidOperationException($"Failed to obtain server command event.").ThrowIfNot(s);
s = EventWaitHandle.TryOpenExisting(responseEventName, out responseEvent);
new InvalidOperationException($"Failed to obtain server response event.").ThrowIfNot(s);
s = EventWaitHandle.TryOpenExisting(processingInProgressName, out processingInProgress);
new InvalidOperationException($"Failed to obtain server progress event.").ThrowIfNot(s);
}
private static MemoryMappedFile FindMMF(string mapName)
{
if (OperatingSystem.IsWindows())
return MemoryMappedFile.OpenExisting(mapName);
return null;
}
public Task<string> QueueCommandReceiveOutput(string command)
{
TaskCompletionSource<string> tcs = new();
if (!Ready)
{
tcs.SetResult("Client is not connected.");
return tcs.Task;
}
processingInProgress.WaitOne();
mutex.WaitOne();
try
{
using (var mmf = FindMMF(ServerName))
using (MemoryMappedViewAccessor accessor = mmf.CreateViewAccessor())
{
byte[] commandBytes = Encoding.UTF8.GetBytes(command);
accessor.Write(0, commandBytes.Length);
accessor.WriteArray(4, commandBytes, 0, commandBytes.Length);
commandEvent.Set();
}
responseEvent.WaitOne();
using (var mmf = FindMMF(ServerName))
using (MemoryMappedViewAccessor accessor = mmf.CreateViewAccessor())
{
int length = accessor.ReadInt32(0);
byte[] outputBytes = new byte[length];
accessor.ReadArray(4, outputBytes, 0, length);
accessor.Write(0, 0);
tcs.SetResult(Encoding.UTF8.GetString(outputBytes));
return tcs.Task;
}
}
finally
{
processingInProgress.Set();
mutex.ReleaseMutex();
}
}
}
[SupportedOSPlatform("windows")]
public class Server
{
public delegate string CommandMethod(string a);
private readonly CommandMethod execution;
internal static List<Server> servers = [];
private Mutex mutex;
public string ServerName { get; private set; }
internal static string Global => (UseGlobals ? @"Global\" : "");
private MemoryMappedFile mmf;
private EventWaitHandle commandEvent;
private EventWaitHandle responseEvent;
private EventWaitHandle processingInProgress;
public Server(string name, CommandMethod executingMethod)
{
execution = executingMethod;
ServerName = Global + name.MakeAlphanumeral();
SetupMMFF();
servers.Add(this);
}
public static string MMFPath
=> OperatingSystem.IsLinux() ?
"/dev/shm" :
OperatingSystem.IsWindows() ?
Path.Combine(
Environment.GetFolderPath(
Environment.SpecialFolder.LocalApplicationData),
"Celeste",
"MMF") :
throw new NotImplementedException(
"Unsupported Platform",
new PlatformNotSupportedException(
"Only supported platforms are linux (being added) and windows."
)
);
[SupportedOSPlatform("windows")]
[UnsupportedOSPlatform("linux", "Use sibling method for linux")]
private void WinCreateFile()
{
new PlatformNotSupportedException("This method is for windows only, if using Linux see the sibling method: LinCreateFile.").ThrowIfNot(OperatingSystem.IsWindows());
mmf = MemoryMappedFile.CreateNew(ServerName, 4096);
}
[SupportedOSPlatform("linux")]
[UnsupportedOSPlatform("windows", "Use sibling method for windows.")]
private void LinCreateFile()
{
// Abstracted since unneeded
}
private void CreateFile(string name)
=> ((Action)WinCreateFile).DoThisIfThatElseDoThis(OperatingSystem.IsWindows(), LinCreateFile);
private static void SetupMMFF()
{
if (!Directory.Exists(MMFPath))
{
Log([$"MMF Path {MMFPath} not found, creating..."]);
Directory.CreateDirectory(MMFPath);
}
}
public bool OpenServer(string name)
{
string
mutexName = Global + name + "_MMFmutex",
commandEventName = Global + name + "_MMFCommandEvent",
responseEventName = Global + name + "_MMFResponseEvent",
processingInProgressName = Global + name + "_MMFProcessingInProgress";
try
{
var e = new InvalidOperationException("Handle already exists! Ensure this is closed before continuing.");
mutex = new Mutex(false, mutexName, out bool good);
e.ThrowIfNot(good);
commandEvent = new EventWaitHandle(false, EventResetMode.AutoReset, commandEventName, out good);
e.ThrowIfNot(good);
responseEvent = new EventWaitHandle(false, EventResetMode.AutoReset, responseEventName, out good);
e.ThrowIfNot(good);
processingInProgress = new EventWaitHandle(false, EventResetMode.ManualReset, processingInProgressName, out good);
e.ThrowIfNot(good);
SetupMMFF();
CreateFile(name);
return true;
}
catch (Exception ex)
{
Console.WriteLine("Failed to open server: " + ex.Message);
return false;
}
}
public void CloseServer()
{
commandEvent?.Dispose();
responseEvent?.Dispose();
processingInProgress?.Dispose();
mutex?.Dispose();
mmf?.Dispose();
commandEvent = null;
responseEvent = null;
processingInProgress = null;
mutex = null;
mmf = null;
}
public string ProcessInput(string command)
=> execution(command) ?? "E4C4D4E.";
public void RunServer()
{
while (true)
{
commandEvent.WaitOne();
mutex.WaitOne();
bool l0 = false;
try
{
processingInProgress.Set();
using (MemoryMappedViewAccessor accessor = mmf.CreateViewAccessor())
{
int length = accessor.ReadInt32(0);
if (length == 0)
{
l0 = true;
responseEvent.Set();
processingInProgress.Reset();
mutex.ReleaseMutex();
continue;
}
byte[] commandBytes = new byte[length];
accessor.ReadArray(4, commandBytes, 0, length);
string command = Encoding.UTF8.GetString(commandBytes);
string result = ProcessInput(command);
byte[] resultBytes = Encoding.UTF8.GetBytes(result);
accessor.Write(0, resultBytes.Length);
accessor.WriteArray(4, resultBytes, 0, resultBytes.Length);
}
if (!l0)
responseEvent.Set();
}
finally
{
if (!l0)
{
processingInProgress.Reset();
mutex.ReleaseMutex();
}
}
}
}
}
// Test Server Project
using System.Text;
using CC = StandardLibrary
internal class Program
{
static async Task Main(string[] args)
{
string pipeName = string.Empty;
bool unipipe = false;
Console.WriteLine("Insert Pipe Name: ");
pipeName = Console.ReadLine();
Console.WriteLine("Non-Unique Pipe?");
unipipe = Console.ReadLine().ToLower().Contains('y');
Console.WriteLine("Global names?");
bool global = Console.ReadLine().ToLower().Contains('y');
var console = CC.Setup(global);
console.OpenConsole();
await Task.Delay(1000);
var server = new CC.Server(pipeName, Command);
await Task.Delay(1000);
}
static string Command(string input)
=> new(((input + " ?skrow").Reverse().ToArray()));
}
// Client test project
using CC = StandardLibrary
internal class Program
{
static async Task Main(string[] args)
{
Console.WriteLine("Global names?");
bool global = Console.ReadLine().ToLower().Contains('y');
CC console = CC.Setup(global);
Console.WriteLine("Insert Server name: ");
string name = Console.ReadLine() ?? "";
CC.Client server = new CC.Client(name);
Console.WriteLine($"Connected to {name}.");
Console.WriteLine("Opening Console...");
console.OpenConsole();
await Task.Delay(1000);
string mes, response;
while (true)
{
CC.Log(["Waiting for message..."]);
Console.WriteLine("Message: ");
mes = Console.ReadLine() ?? "";
CC.Log([$"Sending \"{mes}\"..."]);
response = await server.QueueCommandReceiveOutput(mes);
CC.Log([$"Command output: \"{response}\""]);
Console.WriteLine(response);
}
}
}
Comes up with the FileNotFound @ _ = MemoryMappedFile.OpenExisting(name);
in VerifyServer(name)
when attempting to connect to the server from the client.
Any ideas?
Upvotes: 1
Views: 60