Files
MRCC/launcher/PCL-CE/PCL.Core/IO/PipeComm.cs
T
xyou f70b061d1a
CI / Go Backend (push) Canceled after 0s
feat: 项目初始化 + 3D方块世界原型 + AI助搭系统
初始化 monorepo: Go后端(7微服务) + Unity客户端(9模块) + 启动器

HTML5原型: Three.js 3D体素世界, Perlin噪声地形, 原版材质, 22种方块

Minecraft创造模式背包: 双栏布局, 拖拽移动物品, 方向性元件引脚

AI助搭策划文档 + 客户端/服务端骨架 + Docker Compose + CI
2026-08-08 14:07:56 +08:00

129 lines
5.6 KiB
C#

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);
/// <summary>
/// 用于命名管道通信的统一字符编码
/// </summary>
public static readonly Encoding PipeEncoding = Encoding.UTF8;
/// <summary>
/// 用于命名管道通信的统一终止符
/// </summary>
public const char PipeEndingChar = (char)27; // '\e' (ESC)
/// <summary>
/// 在新的工作线程启动命名管道服务端
/// </summary>
/// <param name="identifier">服务端标识,用于日志标识及工作线程的命名</param>
/// <param name="pipeName">命名管道名称</param>
/// <param name="loopCallback">客户端连接后的回调函数,将会提供用于读取和写入数据的流,以及客户端进程 ID,返回 <c>true</c> 表示继续等待下一个客户端连接,返回 <c>false</c> 则停止服务端运行</param>
/// <param name="stopCallback">服务端停止后的回调函数</param>
/// <param name="stopWhenException">指定当回调函数抛出异常时是否停止服务端运行,使用 <c>true</c> 表示停止</param>
/// <param name="allowedProcessId">允许连接的客户端进程 ID,如为 Nothing 则允许所有</param>
public static NamedPipeServerStream StartPipeServer(string identifier, string pipeName, Func<StreamReader, StreamWriter, Process?, bool> 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;
}
}