⬆️ SDK 1.23.10329.11240 (XmlnsDefinition) & IPC Misc

This commit is contained in:
Aigio Liu
2023-03-29 13:04:26 +08:00
parent 5022f7e847
commit feec9e8b5c
42 changed files with 237 additions and 218 deletions
@@ -7,18 +7,21 @@ public abstract class IPCServiceImpl<TPacket> : IPCService<TPacket>, IDisposable
public async ValueTask<int> RunAsync(string? pipeName)
{
if (pipeClient != null)
if (pipeClient != null) // 此函数应仅调用一次
return (int)IPCExitCode.Ok;
if (string.IsNullOrWhiteSpace(pipeName))
return (int)IPCExitCode.EmptyPipeName;
// 命名管道实现 IPC
pipeClient = new NamedPipeClientStream(".", pipeName, PipeDirection.InOut, PipeOptions.None, TokenImpersonationLevel.Impersonation);
try
{
// 尝试在 45 秒内连接主进程
pipeClient.Connect(TimeSpan.FromSeconds(45d));
// https://blog.lindexi.com/post/dotnet-%E4%BD%BF%E7%94%A8-NamedPipeClientStream-%E8%BF%9E%E6%8E%A5%E4%B8%80%E4%B8%AA%E4%B8%8D%E5%AD%98%E5%9C%A8%E7%AE%A1%E9%81%93%E6%9C%8D%E5%8A%A1%E5%90%8D%E5%B0%86%E4%B8%8D%E6%96%AD%E7%A9%BA%E8%B7%91-CPU-%E8%B5%84%E6%BA%90.html
// 尝试在 15 秒内连接主进程
const int timeout_connect = 15000;
await pipeClient.ConnectAsync(timeout_connect);
}
catch (TimeoutException)
{
@@ -27,7 +30,7 @@ public abstract class IPCServiceImpl<TPacket> : IPCService<TPacket>, IDisposable
while (!disposedValue)
{
var packet = ReadBytes(pipeClient);
var packet = Read(pipeClient);
if (packet != default)
{
var exitCode = await HandleCommand(packet);
@@ -46,14 +49,45 @@ public abstract class IPCServiceImpl<TPacket> : IPCService<TPacket>, IDisposable
Write(pipeClient, packet);
}
/// <summary>
/// 处理数据包
/// </summary>
/// <param name="packet"></param>
/// <returns></returns>
protected virtual ValueTask<int?> HandleCommand(TPacket packet)
{
return default;
}
protected abstract TPacket? ReadBytes(Stream stream);
/// <summary>
/// 从流中读取一个数据包
/// </summary>
/// <param name="stream"></param>
/// <returns></returns>
protected virtual TPacket? Read(Stream stream)
{
Span<byte> inBuffer = stackalloc byte[sizeof(int)];
stream.Read(inBuffer);
var len = BitConverter.ToInt32(inBuffer);
if (len <= 0) return default;
inBuffer = stackalloc byte[len];
stream.Read(inBuffer);
var obj = MemoryPackSerializer.Deserialize<TPacket>(inBuffer);
return obj;
}
protected abstract void Write(Stream stream, TPacket packet);
/// <summary>
/// 将一个数据包写入流中
/// </summary>
/// <param name="stream"></param>
/// <param name="packet"></param>
protected virtual void Write(Stream stream, TPacket packet)
{
byte[] outBuffer = MemoryPackSerializer.Serialize(packet);
stream.Write(BitConverter.GetBytes(outBuffer.Length));
stream.Write(outBuffer);
stream.Flush();
}
void Dispose(bool disposing)
{
@@ -1,6 +1,6 @@
namespace BD.WTTS.Services;
public interface IPCService<TPacket> where TPacket : class
public interface IPCService<TPacket> : IDisposable where TPacket : class
{
/// <summary>
/// 启动 IPC 服务
@@ -24,4 +24,19 @@ public enum ReverseProxyCommand : byte
/// 获取当前流量统计
/// </summary>
GetFlowStatistics,
/// <summary>
/// 调用 UI 进程通知显示 DNS 错误
/// </summary>
NotifyDNSError,
/// <summary>
/// 热重载配置文件
/// </summary>
HotReloadConfig,
/// <summary>
/// 启动反向代理控制台结果
/// </summary>
StartResult,
}
@@ -8,36 +8,8 @@ namespace BD.WTTS.Models;
[MemoryPackUnion(1, typeof(FlowStatistics))]
public partial interface IReverseProxyPacket
{
/// <summary>
/// 从流中读取一个 <see cref="IReverseProxyPacket"/>
/// </summary>
/// <param name="stream"></param>
/// <returns></returns>
static IReverseProxyPacket? ReadBytes(Stream stream)
{
Span<byte> inBuffer = stackalloc byte[sizeof(int)];
stream.Read(inBuffer);
var len = BitConverter.ToInt32(inBuffer);
if (len <= 0) return default;
inBuffer = stackalloc byte[len];
stream.Read(inBuffer);
var obj = MemoryPackSerializer.Deserialize<IReverseProxyPacket>(inBuffer);
return obj;
}
/// <summary>
/// 将一个 <see cref="IReverseProxyPacket"/> 写入流中
/// </summary>
/// <param name="stream"></param>
/// <param name="packet"></param>
static void Write(Stream stream, IReverseProxyPacket packet)
{
byte[] outBuffer = MemoryPackSerializer.Serialize(packet);
stream.Write(BitConverter.GetBytes(outBuffer.Length));
stream.Write(outBuffer);
stream.Flush();
}
}
[MemoryPackable]
public sealed partial record ReverseProxyPacket(ReverseProxyCommand Command) : IReverseProxyPacket { }
public sealed partial record ReverseProxyPacket(ReverseProxyCommand Command, string? Data) : IReverseProxyPacket { }
@@ -1,49 +1,24 @@
if (!args.Any()) return (int)ExitCode.EmptyArrayArgs;
using Microsoft.Extensions.DependencyInjection;
if (!args.Any())
return (int)IPCExitCode.EmptyArrayArgs;
var pipeName = args[0];
if (string.IsNullOrWhiteSpace(pipeName)) return (int)ExitCode.EmptyPipeName;
if (string.IsNullOrWhiteSpace(pipeName))
return (int)IPCExitCode.EmptyPipeName;
using var pipeClient = new NamedPipeClientStream(".", pipeName, PipeDirection.InOut, PipeOptions.None, TokenImpersonationLevel.Impersonation);
Ioc.ConfigureServices(ConfigureServices);
try
static void ConfigureServices(IServiceCollection services)
{
// 尝试在 45 秒内连接主进程
pipeClient.Connect(TimeSpan.FromSeconds(45d));
}
catch (TimeoutException)
{
return (int)ExitCode.ConnectServerTimeout;
}
while (true)
{
var packet = IReverseProxyPacket.ReadBytes(pipeClient);
if (packet != default)
services.AddLogging(l =>
{
if (packet is ReverseProxyPacket packet1)
{
switch (packet1.Command)
{
case ReverseProxyCommand.Exit:
goto exit;
case ReverseProxyCommand.GetFlowStatistics:
FlowStatistics flowStatistics = null!;
IReverseProxyPacket.Write(pipeClient, flowStatistics);
break;
case ReverseProxyCommand.Start:
break;
case ReverseProxyCommand.Stop:
break;
}
}
}
l.AddConsole();
});
services.AddReverseProxyService();
services.AddSingleton<IPCService, IPCServiceImpl>();
}
exit: return (int)ExitCode.Ok;
enum ExitCode
{
Ok = 0,
EmptyArrayArgs = 4001,
EmptyPipeName,
ConnectServerTimeout,
}
using var ipc = IPCService.Instance;
var exitCode = await ipc.RunAsync(pipeName);
return exitCode;
@@ -9,11 +9,13 @@ namespace BD.WTTS.Services.Implementation;
sealed class DomainResolver : IDomainResolver
{
readonly IReverseProxyConfig reverseProxyConfig;
readonly IPCService ipc;
bool isIpv6 = false;
public DomainResolver(IReverseProxyConfig reverseProxyConfig)
public DomainResolver(IPCService ipc, IReverseProxyConfig reverseProxyConfig)
{
this.ipc = ipc;
this.reverseProxyConfig = reverseProxyConfig;
}
@@ -34,7 +36,7 @@ sealed class DomainResolver : IDomainResolver
}
catch (Exception ex)
{
IPCService.Instance.NotifyDNSError(ex);
ipc.Send(ReverseProxyCommand.NotifyDNSError, ex.Message);
//INotificationService.Instance.Notify(AppResources.CommunityFix_DNSErrorNotify + Environment.NewLine + "Exception Message :" + ex.Message, NotificationType.Message);
throw;
}
@@ -2,21 +2,31 @@ namespace BD.WTTS.Services.Implementation;
sealed class IPCServiceImpl : IPCServiceImpl<IReverseProxyPacket>, IPCService
{
protected override ValueTask<int?> HandleCommand(IReverseProxyPacket packet)
public IPCServiceImpl()
{
// 保持无依赖注入的空构造函数
}
protected override async ValueTask<int?> HandleCommand(IReverseProxyPacket packet)
{
if (packet is ReverseProxyPacket packet1)
{
switch (packet1.Command)
{
case ReverseProxyCommand.Exit:
return new ValueTask<int?>((int)IPCExitCode.Ok);
return (int)IPCExitCode.Ok;
case ReverseProxyCommand.GetFlowStatistics:
FlowStatistics flowStatistics = null!;
Send(flowStatistics);
break;
case ReverseProxyCommand.Start:
var startResult = await IReverseProxyService.Instance.StartProxyAsync();
Send(ReverseProxyCommand.StartResult, startResult ? null : "Startup failed.");
break;
case ReverseProxyCommand.Stop:
await IReverseProxyService.Instance.StopProxyAsync();
break;
case ReverseProxyCommand.HotReloadConfig:
break;
}
}
@@ -24,18 +34,9 @@ sealed class IPCServiceImpl : IPCServiceImpl<IReverseProxyPacket>, IPCService
return default;
}
public void NotifyDNSError(Exception ex)
public void Send(ReverseProxyCommand command, string? data = null)
{
}
protected override IReverseProxyPacket? ReadBytes(Stream stream)
{
return IReverseProxyPacket.ReadBytes(stream);
}
protected override void Write(Stream stream, IReverseProxyPacket packet)
{
IReverseProxyPacket.Write(stream, packet);
ReverseProxyPacket packet = new(command, data);
Send(packet);
}
}
@@ -28,6 +28,7 @@ partial class YarpReverseProxyServiceImpl
services.AddPacketIntercept();
}
#endif
services.AddSingleton<IPCService>(_ => ipc);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -11,11 +11,14 @@ sealed partial class YarpReverseProxyServiceImpl : ReverseProxyServiceImpl, IRev
static readonly string RootPath = Path.Combine(IOPath.AppDataDirectory, "Yarp");
WebApplication? app;
readonly IPCService ipc;
public YarpReverseProxyServiceImpl(
IPCService ipc,
IDnsAnalysisService dnsAnalysis,
ICertificateManager certificateManager) : base(dnsAnalysis)
{
this.ipc = ipc;
CertificateManager = certificateManager;
}
@@ -4,5 +4,10 @@ public interface IPCService : IPCService<IReverseProxyPacket>
{
static IPCService Instance => Ioc.Get<IPCService>();
void NotifyDNSError(Exception ex);
/// <summary>
/// 给主进程发送指令以及可选的附加消息
/// </summary>
/// <param name="command"></param>
/// <param name="data"></param>
void Send(ReverseProxyCommand command, string? data = null);
}
@@ -0,0 +1,123 @@
namespace BD.WTTS.Models.Abstractions;
/// <summary>
/// 操作结果信息类,对操作结果进行封装
/// </summary>
public interface IOperationResult
{
/// <summary>
/// 获取或设置 操作结果类型
/// </summary>
OperationResultType ResultType { get; set; }
/// <summary>
/// 获取或设置 操作返回信息
/// </summary>
string Message { get; set; }
/// <summary>
/// 获取或设置 操作返回的日志消息,用于记录日志
/// </summary>
string LogMessage { get; set; }
}
/// <inheritdoc cref="IOperationResult"/>
public interface IOperationResult<T> : IOperationResult
{
/// <summary>
/// 获取或设置 操作结果附加信息
/// </summary>
T AppendData { get; set; }
}
/// <inheritdoc cref="IOperationResult{T}"/>
public abstract class OperationResultBase<T> : IOperationResult<T>
{
#region 构造函数
public OperationResultBase()
{
ResultType = OperationResultType.Error;
}
/// <summary>
/// 初始化一个 操作结果信息类 的新实例
/// </summary>
/// <param name="resultType">操作结果类型</param>
public OperationResultBase(OperationResultType resultType)
{
ResultType = resultType;
}
/// <summary>
/// 初始化一个 定义返回消息的操作结果信息类 的新实例
/// </summary>
/// <param name="resultType">操作结果类型</param>
/// <param name="message">返回消息</param>
public OperationResultBase(OperationResultType resultType, string message)
: this(resultType)
{
Message = message;
}
/// <summary>
/// 初始化一个 定义返回消息与附加数据的操作结果信息类 的新实例
/// </summary>
/// <param name="resultType">操作结果类型</param>
/// <param name="message">返回消息</param>
/// <param name="appendData">返回数据</param>
public OperationResultBase(OperationResultType resultType, string message, T? appendData)
: this(resultType, message)
{
_AppendData = appendData;
}
/// <summary>
/// 初始化一个 定义返回消息与日志消息的操作结果信息类 的新实例
/// </summary>
/// <param name="resultType">操作结果类型</param>
/// <param name="message">返回消息</param>
/// <param name="logMessage">日志记录消息</param>
public OperationResultBase(OperationResultType resultType, string message, string logMessage)
: this(resultType, message)
{
LogMessage = logMessage;
}
/// <summary>
/// 初始化一个 定义返回消息、日志消息与附加数据的操作结果信息类 的新实例
/// </summary>
/// <param name="resultType">操作结果类型</param>
/// <param name="message">返回消息</param>
/// <param name="logMessage">日志记录消息</param>
/// <param name="appendData">返回数据</param>
public OperationResultBase(OperationResultType resultType, string message, string logMessage, T? appendData)
: this(resultType, message, logMessage)
{
_AppendData = appendData;
}
#endregion
#region 属性
public OperationResultType ResultType { get; set; }
public string Message { get; set; } = string.Empty;
public string LogMessage { get; set; } = string.Empty;
protected T? _AppendData;
public virtual T AppendData
{
get
{
_AppendData ??= Activator.CreateInstance<T>();
return _AppendData;
}
set => _AppendData = value;
}
#endregion
}
@@ -1,127 +1,5 @@
namespace BD.WTTS.Models;
/// <summary>
/// 操作结果信息类,对操作结果进行封装
/// </summary>
public interface IOperationResult
{
/// <summary>
/// 获取或设置 操作结果类型
/// </summary>
OperationResultType ResultType { get; set; }
/// <summary>
/// 获取或设置 操作返回信息
/// </summary>
string Message { get; set; }
/// <summary>
/// 获取或设置 操作返回的日志消息,用于记录日志
/// </summary>
string LogMessage { get; set; }
}
/// <inheritdoc cref="IOperationResult"/>
public interface IOperationResult<T> : IOperationResult
{
/// <summary>
/// 获取或设置 操作结果附加信息
/// </summary>
T AppendData { get; set; }
}
/// <inheritdoc cref="IOperationResult{T}"/>
public abstract class OperationResultBase<T> : IOperationResult<T>
{
#region 构造函数
public OperationResultBase()
{
ResultType = OperationResultType.Error;
}
/// <summary>
/// 初始化一个 操作结果信息类 的新实例
/// </summary>
/// <param name="resultType">操作结果类型</param>
public OperationResultBase(OperationResultType resultType)
{
ResultType = resultType;
}
/// <summary>
/// 初始化一个 定义返回消息的操作结果信息类 的新实例
/// </summary>
/// <param name="resultType">操作结果类型</param>
/// <param name="message">返回消息</param>
public OperationResultBase(OperationResultType resultType, string message)
: this(resultType)
{
Message = message;
}
/// <summary>
/// 初始化一个 定义返回消息与附加数据的操作结果信息类 的新实例
/// </summary>
/// <param name="resultType">操作结果类型</param>
/// <param name="message">返回消息</param>
/// <param name="appendData">返回数据</param>
public OperationResultBase(OperationResultType resultType, string message, T? appendData)
: this(resultType, message)
{
_AppendData = appendData;
}
/// <summary>
/// 初始化一个 定义返回消息与日志消息的操作结果信息类 的新实例
/// </summary>
/// <param name="resultType">操作结果类型</param>
/// <param name="message">返回消息</param>
/// <param name="logMessage">日志记录消息</param>
public OperationResultBase(OperationResultType resultType, string message, string logMessage)
: this(resultType, message)
{
LogMessage = logMessage;
}
/// <summary>
/// 初始化一个 定义返回消息、日志消息与附加数据的操作结果信息类 的新实例
/// </summary>
/// <param name="resultType">操作结果类型</param>
/// <param name="message">返回消息</param>
/// <param name="logMessage">日志记录消息</param>
/// <param name="appendData">返回数据</param>
public OperationResultBase(OperationResultType resultType, string message, string logMessage, T? appendData)
: this(resultType, message, logMessage)
{
_AppendData = appendData;
}
#endregion
#region 属性
public OperationResultType ResultType { get; set; }
public string Message { get; set; } = string.Empty;
public string LogMessage { get; set; } = string.Empty;
protected T? _AppendData;
public virtual T AppendData
{
get
{
_AppendData ??= Activator.CreateInstance<T>();
return _AppendData;
}
set => _AppendData = value;
}
#endregion
}
/// <inheritdoc cref="OperationResultBase{T}"/>
public class OperationResult : OperationResultBase<object>
{
@@ -0,0 +1,10 @@
#if (WINDOWS || MACCATALYST || MACOS || LINUX) && !(IOS || ANDROID)
using XmlnsDefinition = Avalonia.Metadata.XmlnsDefinitionAttribute;
// steampp
[assembly: XmlnsDefinition("https://steampp.net/ui", "BD.WTTS.Models")]
[assembly: XmlnsDefinition("https://steampp.net/ui", "BD.WTTS.Models.Abstractions")]
[assembly: XmlnsDefinition("https://steampp.net/ui", "BD.WTTS.UI.ViewModels")]
[assembly: XmlnsDefinition("https://steampp.net/ui", "BD.WTTS.UI.ViewModels.Abstractions")]
#endif