初始化 monorepo: Go后端(7微服务) + Unity客户端(9模块) + 启动器 HTML5原型: Three.js 3D体素世界, Perlin噪声地形, 原版材质, 22种方块 Minecraft创造模式背包: 双栏布局, 拖拽移动物品, 方向性元件引脚 AI助搭策划文档 + 客户端/服务端骨架 + Docker Compose + CI
This commit is contained in:
@@ -0,0 +1,128 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user