using System; using System.Diagnostics; using System.IO; using System.IO.Pipes; using System.Linq; using System.Text; using PCL.Core.App; using PCL.Core.Logging; using PCL.Core.Utils.OS; namespace PCL.Core.IO; public static class PipeComm { private static void _PipeLog(string message) => LogWrapper.Trace("Pipe", message); private static void _PipeLogDebug(string message) => LogWrapper.Debug("Pipe", message); /// /// 用于命名管道通信的统一字符编码 /// public static readonly Encoding PipeEncoding = Encoding.UTF8; /// /// 用于命名管道通信的统一终止符 /// public const char PipeEndingChar = (char)27; // '\e' (ESC) /// /// 在新的工作线程启动命名管道服务端 /// /// 服务端标识,用于日志标识及工作线程的命名 /// 命名管道名称 /// 客户端连接后的回调函数,将会提供用于读取和写入数据的流,以及客户端进程 ID,返回 true 表示继续等待下一个客户端连接,返回 false 则停止服务端运行 /// 服务端停止后的回调函数 /// 指定当回调函数抛出异常时是否停止服务端运行,使用 true 表示停止 /// 允许连接的客户端进程 ID,如为 Nothing 则允许所有 public static NamedPipeServerStream StartPipeServer(string identifier, string pipeName, Func loopCallback, Action? stopCallback = null, bool stopWhenException = false, int[]? allowedProcessId = null) { var pipe = new NamedPipeServerStream(pipeName, PipeDirection.InOut, 1, PipeTransmissionMode.Byte, PipeOptions.None, 1024, 1024); var threadName = $"PipeServer/{identifier}"; Basics.RunInNewThread(() => { LogWrapper.Debug("Pipe", $"{identifier}: {pipeName} 服务端已在 '{threadName}' 工作线程启动"); var hasNextLoop = true; var connected = false; while (hasNextLoop) { try { hasNextLoop = false; pipe.WaitForConnection(); // 等待客户端连接 // 获取客户端进程实例并校验 Process? clientProcess = null; var clientProcessId = 0; try { var pid = KernelInterop.GetNamedPipeClientProcessId(pipe.SafePipeHandle.DangerousGetHandle()); clientProcessId = (int)pid; if (allowedProcessId is not null) { var denied = allowedProcessId.All(id => id != clientProcessId); if (denied) { hasNextLoop = true; pipe.Disconnect(); _PipeLog($"[Pipe] {identifier}: 已拒绝 {clientProcessId}"); continue; } } clientProcess = Process.GetProcessById(clientProcessId); } catch (Exception) { if (allowedProcessId is not null) { hasNextLoop = true; throw; } } connected = true; LogWrapper.Debug("Pipe", $"{identifier}: {clientProcessId} 已连接"); // 初始化读取/写入流 var reader = new StreamReader(pipe, PipeEncoding, false, 1024, true); var writer = new StreamWriter(pipe, PipeEncoding, 1024, true); // 执行回调函数 hasNextLoop = loopCallback(reader, writer, clientProcess); // 写入终止符 writer.Write(PipeEndingChar); writer.Flush(); // 刷新写入缓冲 reader.Read(); // 等待客户端 } catch (Exception ex) { if (!pipe.IsConnected && connected && ex is IOException) { _PipeLogDebug($"{identifier}: 客户端连接已丢失"); hasNextLoop = true; } else { LogWrapper.Warn(ex, "Pipe", $"{identifier}: 服务端出错"); if (stopWhenException) hasNextLoop = false; } } try { pipe.Disconnect(); } catch (InvalidOperationException) { // 由于没妈的巨硬给的 IsConnected 不一定是准确的,需要运行 Disconnect() 确保管道断开连接 // 如果已经断开会抛出 InvalidOperationException 这里直接忽略掉 } connected = false; _PipeLogDebug($"{identifier}: 已断开连接"); } // 释放资源并执行停止回调 pipe.Dispose(); _PipeLogDebug($"{identifier}: 服务端已停止"); stopCallback?.Invoke(); }, threadName); return pipe; } }