del /AudioWallpaper.1.2.1.nupkg

This commit is contained in:
2024-10-18 08:30:59 +08:00
parent d351e442ab
commit dba24c0cbb
3773 changed files with 8960 additions and 79217 deletions

View File

@@ -0,0 +1,211 @@
using IpcLibrary.Connection;
using IpcLibrary.Core;
using IpcLibrary.Core.Exceptions;
using IpcLibrary.Serialization;
using IpcLibrary.Services;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace IpcLibrary.Client {
/// <summary>
/// IPC客户端实现
/// </summary>
public class IPCClient : IIPCClient {
private readonly NamedPipeConnectionManager _connectionManager;
private readonly IServiceRegistry _serviceRegistry;
private readonly HeartbeatManager _heartbeatManager;
private readonly MessageRouter _messageRouter;
private readonly IPCConfiguration _configuration;
private readonly string _processId;
private bool _disposed;
public string ProcessId => _processId;
public bool IsConnected { get; private set; }
public event EventHandler<ProcessInfo> ProcessConnected;
public event EventHandler<ProcessInfo> ProcessDisconnected;
public event EventHandler<IPCMessage> MessageReceived;
public IPCClient(IPCConfiguration configuration = null) {
_configuration = configuration ?? new IPCConfiguration();
_processId = $"Client_{Environment.ProcessId}_{Guid.NewGuid():N}";
var serializer = new CompositeMessageSerializer();
_connectionManager = new NamedPipeConnectionManager(serializer, _configuration, "IPCPipe");
_serviceRegistry = new ServiceRegistry();
_heartbeatManager = new HeartbeatManager();
var methodCallHandler = new MethodCallHandler(_serviceRegistry);
_messageRouter = new MessageRouter(methodCallHandler);
InitializeEventHandlers();
}
public async Task<bool> ConnectAsync(string serverAddress) {
try {
var success = await _connectionManager.ConnectToProcessAsync(_processId, serverAddress);
if (success) {
IsConnected = true;
if (_configuration.EnableHeartbeat) {
_heartbeatManager.StartMonitoring(_processId);
_ = Task.Run(SendHeartbeats);
}
}
return success;
} catch (Exception ex) {
throw new ConnectionException($"连接失败: {ex.Message}", ex);
}
}
public async Task DisconnectAsync() {
if (!IsConnected)
return;
try {
_heartbeatManager.StopMonitoring(_processId);
await _connectionManager.DisconnectFromProcessAsync(_processId);
IsConnected = false;
} catch (Exception ex) {
throw new ConnectionException($"断开连接失败: {ex.Message}", ex);
}
}
public async Task<T> CallMethodAsync<T>(string targetProcessId, string serviceName, string methodName, params object[] parameters) {
var result = await CallMethodAsync(targetProcessId, serviceName, methodName, typeof(T), parameters);
return result is T typedResult ? typedResult : default(T);
}
public async Task<object> CallMethodAsync(string targetProcessId, string serviceName, string methodName, Type returnType, params object[] parameters) {
if (!IsConnected)
throw new ConnectionException("客户端未连接");
try {
var request = new MethodCallRequest {
ServiceName = serviceName,
MethodName = methodName,
Parameters = parameters,
ParameterTypes = parameters != null ? Array.ConvertAll(parameters, p => p?.GetType()) : new Type[0]
};
var message = new IPCMessage {
Type = MessageType.Request,
Method = "CallMethod",
Parameters = new object[] { request },
SourceProcessId = _processId,
TargetProcessId = targetProcessId
};
var response = await _messageRouter.SendRequestAsync(
message,
msg => _connectionManager.SendMessageAsync(targetProcessId, msg),
_configuration.MethodCallTimeout);
if (response.Type == MessageType.Error) {
throw new MethodCallException(response.Error);
}
if (response.Result is MethodCallResponse methodResponse) {
if (!methodResponse.IsSuccess) {
throw new MethodCallException("方法调用失败", methodResponse.Exception);
}
return methodResponse.Result;
}
return response.Result;
} catch (Exception ex) when (!(ex is IPCException)) {
throw new MethodCallException($"调用方法失败: {ex.Message}", ex);
}
}
public async Task SendNotificationAsync(string targetProcessId, string method, params object[] parameters) {
if (!IsConnected)
throw new ConnectionException("客户端未连接");
var message = new IPCMessage {
Type = MessageType.Notification,
Method = method,
Parameters = parameters,
SourceProcessId = _processId,
TargetProcessId = targetProcessId
};
await _connectionManager.SendMessageAsync(targetProcessId, message);
}
public async Task RegisterServiceAsync<T>(string serviceName, T serviceInstance) {
await _serviceRegistry.RegisterServiceAsync(serviceName, serviceInstance);
}
public async Task UnregisterServiceAsync(string serviceName) {
await _serviceRegistry.UnregisterServiceAsync(serviceName);
}
private void InitializeEventHandlers() {
_connectionManager.ProcessConnected += (sender, info) => ProcessConnected?.Invoke(this, info);
_connectionManager.ProcessDisconnected += (sender, info) => ProcessDisconnected?.Invoke(this, info);
_heartbeatManager.ProcessTimeout += async (sender, processId) => {
if (processId == _processId) {
IsConnected = false;
if (_configuration.EnableAutoReconnect) {
await TryReconnectAsync();
}
}
};
}
private async Task SendHeartbeats() {
while (IsConnected && !_disposed) {
try {
var heartbeatMessage = new IPCMessage {
Type = MessageType.Heartbeat,
SourceProcessId = _processId
};
await _connectionManager.BroadcastMessageAsync(heartbeatMessage);
await Task.Delay(_configuration.HeartbeatInterval);
} catch (Exception ex) {
Console.WriteLine($"发送心跳失败: {ex.Message}");
break;
}
}
}
private async Task TryReconnectAsync() {
for (int attempt = 0; attempt < _configuration.MaxRetryAttempts; attempt++) {
try {
await Task.Delay(_configuration.RetryDelay);
if (await ConnectAsync("default")) {
break;
}
} catch (Exception ex) {
Console.WriteLine($"重连尝试 {attempt + 1} 失败: {ex.Message}");
}
}
}
public void Dispose() {
if (_disposed)
return;
_disposed = true;
try {
DisconnectAsync().Wait(TimeSpan.FromSeconds(5));
} catch (Exception ex) {
Console.WriteLine($"释放资源时发生错误: {ex.Message}");
}
_connectionManager?.Dispose();
_heartbeatManager?.Dispose();
}
}
}

View File

@@ -0,0 +1,65 @@
using IpcLibrary.Core;
using IpcLibrary.Core.Exceptions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace IpcLibrary.Client {
/// <summary>
/// 客户端扩展方法
/// </summary>
public static class IPCClientExtensions {
/// <summary>
/// 调用无返回值的方法
/// </summary>
public static async Task CallMethodAsync(this IIPCClient client, string targetProcessId, string serviceName, string methodName, params object[] parameters) {
await client.CallMethodAsync<object>(targetProcessId, serviceName, methodName, parameters);
}
/// <summary>
/// 注册多个服务
/// </summary>
public static async Task RegisterServicesAsync(this IIPCClient client, Dictionary<string, object> services) {
foreach (var kvp in services) {
await client.RegisterServiceAsync(kvp.Key, kvp.Value);
}
}
/// <summary>
/// 批量发送通知
/// </summary>
public static async Task SendNotificationsAsync(this IIPCClient client, IEnumerable<string> targetProcessIds, string method, params object[] parameters) {
var tasks = new List<Task>();
foreach (var processId in targetProcessIds) {
tasks.Add(client.SendNotificationAsync(processId, method, parameters));
}
await Task.WhenAll(tasks);
}
/// <summary>
/// 使用重试机制调用方法
/// </summary>
public static async Task<T> CallMethodWithRetryAsync<T>(this IIPCClient client, string targetProcessId, string serviceName, string methodName, int maxRetries = 3, TimeSpan? retryDelay = null, params object[] parameters) {
var delay = retryDelay ?? TimeSpan.FromSeconds(1);
Exception lastException = null;
for (int attempt = 0; attempt <= maxRetries; attempt++) {
try {
return await client.CallMethodAsync<T>(targetProcessId, serviceName, methodName, parameters);
} catch (Exception ex) {
lastException = ex;
if (attempt < maxRetries) {
await Task.Delay(delay);
}
}
}
throw new MethodCallException($"方法调用在 {maxRetries + 1} 次尝试后仍然失败", lastException);
}
}
}

View File

@@ -0,0 +1,42 @@
using IpcLibrary.Core;
using IpcLibrary.Core.Exceptions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace IpcLibrary.Client {
/// <summary>
/// IPC客户端工厂
/// </summary>
public static class IPCClientFactory {
/// <summary>
/// 创建默认配置的客户端
/// </summary>
public static IIPCClient CreateClient() {
return new IPCClient();
}
/// <summary>
/// 创建自定义配置的客户端
/// </summary>
public static IIPCClient CreateClient(IPCConfiguration configuration) {
return new IPCClient(configuration);
}
/// <summary>
/// 创建并连接客户端
/// </summary>
public static async Task<IIPCClient> CreateAndConnectAsync(string serverAddress, IPCConfiguration configuration = null) {
var client = new IPCClient(configuration);
if (!await client.ConnectAsync(serverAddress)) {
client.Dispose();
throw new ConnectionException("无法连接到服务器");
}
return client;
}
}
}

View File

@@ -0,0 +1,198 @@
using IpcLibrary.Core;
using IpcLibrary.Core.Exceptions;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO.Pipes;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace IpcLibrary.Connection {
/// <summary>
/// 基于命名管道的连接管理器
/// </summary>
public class NamedPipeConnectionManager : IConnectionManager, IDisposable {
private readonly ConcurrentDictionary<string, ProcessConnection> _connections;
private readonly IMessageSerializer _serializer;
private readonly IPCConfiguration _configuration;
private readonly string _serverPipeName;
private readonly CancellationTokenSource _cancellationTokenSource;
private NamedPipeServerStream _serverPipe;
private bool _isServer;
private bool _disposed;
public event EventHandler<ProcessInfo> ProcessConnected;
public event EventHandler<ProcessInfo> ProcessDisconnected;
public NamedPipeConnectionManager(IMessageSerializer serializer, IPCConfiguration configuration, string pipeName) {
_connections = new ConcurrentDictionary<string, ProcessConnection>();
_serializer = serializer ?? throw new ArgumentNullException(nameof(serializer));
_configuration = configuration ?? throw new ArgumentNullException(nameof(configuration));
_serverPipeName = pipeName ?? throw new ArgumentNullException(nameof(pipeName));
_cancellationTokenSource = new CancellationTokenSource();
}
public async Task StartServerAsync() {
if (_isServer)
return;
_isServer = true;
Console.WriteLine("123456");
await Task.Run(() => ListenForConnections(_cancellationTokenSource.Token));
}
public async Task<bool> ConnectToProcessAsync(string processId, string address) {
try {
if (_connections.ContainsKey(processId))
return true;
var clientPipe = new NamedPipeClientStream(".", _serverPipeName, PipeDirection.InOut, PipeOptions.Asynchronous);
Console.WriteLine("00000000000");
await clientPipe.ConnectAsync(_configuration.ConnectionTimeout, _cancellationTokenSource.Token);
Console.WriteLine("1111111111");
var connection = new ProcessConnection(processId, clientPipe, _serializer);
connection.MessageReceived += OnConnectionMessageReceived;
connection.Disconnected += OnConnectionDisconnected;
if (_connections.TryAdd(processId, connection)) {
Console.WriteLine("00000000000");
await connection.StartAsync(_cancellationTokenSource.Token);
Console.WriteLine("000001000000");
var processInfo = new ProcessInfo {
ProcessId = processId,
Status = ProcessStatus.Connected,
LastHeartbeat = DateTime.UtcNow
};
ProcessConnected?.Invoke(this, processInfo);
return true;
}
return false;
} catch (Exception ex) {
throw new ConnectionException($"连接到进程 {processId} 失败: {ex.Message}", ex);
}
}
public async Task DisconnectFromProcessAsync(string processId) {
if (_connections.TryRemove(processId, out var connection)) {
await connection.DisconnectAsync();
var processInfo = new ProcessInfo {
ProcessId = processId,
Status = ProcessStatus.Disconnected,
LastHeartbeat = DateTime.UtcNow
};
ProcessDisconnected?.Invoke(this, processInfo);
}
}
public async Task<bool> SendMessageAsync(string targetProcessId, IPCMessage message) {
if (_connections.TryGetValue(targetProcessId, out var connection)) {
return await connection.SendMessageAsync(message);
}
return false;
}
public async Task BroadcastMessageAsync(IPCMessage message) {
var tasks = _connections.Values.Select(conn => conn.SendMessageAsync(message));
await Task.WhenAll(tasks);
}
public ProcessInfo GetProcessInfo(string processId) {
if (_connections.TryGetValue(processId, out var connection)) {
return connection.GetProcessInfo();
}
return null;
}
public IReadOnlyList<ProcessInfo> GetAllProcesses() {
return _connections.Values.Select(conn => conn.GetProcessInfo()).ToList();
}
private async Task ListenForConnections(CancellationToken cancellationToken) {
while (!cancellationToken.IsCancellationRequested) {
try {
_serverPipe = new NamedPipeServerStream(_serverPipeName, PipeDirection.InOut,
_configuration.MaxConcurrentConnections, PipeTransmissionMode.Byte, PipeOptions.Asynchronous);
await _serverPipe.WaitForConnectionAsync(cancellationToken);
var processId = Guid.NewGuid().ToString();
var connection = new ProcessConnection(processId, _serverPipe, _serializer);
connection.MessageReceived += OnConnectionMessageReceived;
connection.Disconnected += OnConnectionDisconnected;
if (_connections.TryAdd(processId, connection)) {
Console.WriteLine(123);
await connection.StartAsync(cancellationToken);
Console.WriteLine(254);
var processInfo = new ProcessInfo {
ProcessId = processId,
Status = ProcessStatus.Connected,
LastHeartbeat = DateTime.UtcNow
};
ProcessConnected?.Invoke(this, processInfo);
}
} catch (OperationCanceledException) {
break;
} catch (Exception ex) {
// 记录错误,继续监听
Console.WriteLine($"监听连接时发生错误: {ex.Message}");
await Task.Delay(1000, cancellationToken);
}
}
}
private void OnConnectionMessageReceived(object sender, IPCMessage message) {
// 消息将由上层处理
}
private async void OnConnectionDisconnected(object sender, EventArgs e) {
if (sender is ProcessConnection connection) {
var processId = connection.ProcessId;
_connections.TryRemove(processId, out _);
var processInfo = new ProcessInfo {
ProcessId = processId,
Status = ProcessStatus.Disconnected,
LastHeartbeat = DateTime.UtcNow
};
ProcessDisconnected?.Invoke(this, processInfo);
// 如果启用自动重连
if (_configuration.EnableAutoReconnect && !_disposed) {
await TryReconnectAsync(processId);
}
}
}
private async Task TryReconnectAsync(string processId) {
for (int attempt = 0; attempt < _configuration.MaxRetryAttempts; attempt++) {
try {
await Task.Delay(_configuration.RetryDelay);
if (await ConnectToProcessAsync(processId, _serverPipeName)) {
break;
}
} catch (Exception ex) {
Console.WriteLine($"重连尝试 {attempt + 1} 失败: {ex.Message}");
}
}
}
public void Dispose() {
if (_disposed)
return;
_disposed = true;
_cancellationTokenSource.Cancel();
_cancellationTokenSource?.Dispose();
}
}
}

View File

@@ -0,0 +1,133 @@
using IpcLibrary.Core;
using System.IO.Pipes;
namespace IpcLibrary.Connection {
/// <summary>
/// 单个进程连接
/// </summary>
internal class ProcessConnection : IDisposable {
private readonly string _processId;
private readonly PipeStream _pipe;
private readonly IMessageSerializer _serializer;
private readonly CancellationTokenSource _cancellationTokenSource;
private readonly SemaphoreSlim _sendSemaphore;
private bool _disposed;
public string ProcessId => _processId;
public bool IsConnected => _pipe?.IsConnected == true;
public event EventHandler<IPCMessage> MessageReceived;
public event EventHandler Disconnected;
public ProcessConnection(string processId, PipeStream pipe, IMessageSerializer serializer) {
_processId = processId ?? throw new ArgumentNullException(nameof(processId));
_pipe = pipe ?? throw new ArgumentNullException(nameof(pipe));
_serializer = serializer ?? throw new ArgumentNullException(nameof(serializer));
_cancellationTokenSource = new CancellationTokenSource();
_sendSemaphore = new SemaphoreSlim(1, 1);
}
public async Task StartAsync(CancellationToken cancellationToken) {
var combinedToken = CancellationTokenSource.CreateLinkedTokenSource(
cancellationToken, _cancellationTokenSource.Token).Token;
Task.Run(() => ReceiveMessages(combinedToken), combinedToken);
}
public async Task<bool> SendMessageAsync(IPCMessage message) {
if (!IsConnected || _disposed)
return false;
await _sendSemaphore.WaitAsync();
try {
var data = _serializer.Serialize(message);
var lengthBytes = BitConverter.GetBytes(data.Length);
await _pipe.WriteAsync(lengthBytes, 0, lengthBytes.Length);
await _pipe.WriteAsync(data, 0, data.Length);
await _pipe.FlushAsync();
return true;
} catch (Exception ex) {
Console.WriteLine($"发送消息失败: {ex.Message}");
return false;
} finally {
_sendSemaphore.Release();
}
}
public async Task DisconnectAsync() {
if (_disposed)
return;
_cancellationTokenSource.Cancel();
try {
_pipe?.Close();
} catch (Exception ex) {
Console.WriteLine($"关闭管道时发生错误: {ex.Message}");
}
Disconnected?.Invoke(this, EventArgs.Empty);
}
public ProcessInfo GetProcessInfo() {
return new ProcessInfo {
ProcessId = _processId,
Status = IsConnected ? ProcessStatus.Connected : ProcessStatus.Disconnected,
LastHeartbeat = DateTime.UtcNow
};
}
private async Task ReceiveMessages(CancellationToken cancellationToken) {
var lengthBuffer = new byte[4];
while (!cancellationToken.IsCancellationRequested && IsConnected) {
try {
// 读取消息长度
var bytesRead = await _pipe.ReadAsync(lengthBuffer, 0, 4, cancellationToken);
if (bytesRead != 4)
break;
var messageLength = BitConverter.ToInt32(lengthBuffer, 0);
if (messageLength <= 0 || messageLength > 1024 * 1024) // 限制最大消息大小
break;
// 读取消息内容
var messageBuffer = new byte[messageLength];
var totalBytesRead = 0;
while (totalBytesRead < messageLength && !cancellationToken.IsCancellationRequested) {
var bytes = await _pipe.ReadAsync(messageBuffer, totalBytesRead,
messageLength - totalBytesRead, cancellationToken);
if (bytes == 0)
break;
totalBytesRead += bytes;
}
if (totalBytesRead == messageLength) {
var message = _serializer.Deserialize<IPCMessage>(messageBuffer);
MessageReceived?.Invoke(this, message);
}
} catch (OperationCanceledException) {
break;
} catch (Exception ex) {
Console.WriteLine($"接收消息时发生错误: {ex.Message}");
break;
}
}
await DisconnectAsync();
}
public void Dispose() {
if (_disposed) return;
_disposed = true;
_cancellationTokenSource.Cancel();
_cancellationTokenSource.Dispose();
}
}
}

View File

@@ -0,0 +1,36 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace IpcLibrary.Core.Exceptions {
/// <summary>
/// IPC异常基类
/// </summary>
public class IPCException : Exception {
public IPCException(string message) : base(message) { }
public IPCException(string message, Exception innerException) : base(message, innerException) { }
}
/// <summary>
/// 连接异常
/// </summary>
public class ConnectionException : IPCException {
public ConnectionException(string message) : base(message) { }
public ConnectionException(string message, Exception innerException) : base(message, innerException) { }
}
/// <summary>
/// 方法调用异常
/// </summary>
public class MethodCallException : IPCException {
public MethodCallException(string message) : base(message) { }
public MethodCallException(string message, Exception innerException) : base(message, innerException) { }
}
/// <summary>
/// 超时异常
/// </summary>
public class TimeoutException : IPCException {
public TimeoutException(string message) : base(message) { }
public TimeoutException(string message, Exception innerException) : base(message, innerException) { }
}
}

View File

@@ -0,0 +1,22 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace IpcLibrary.Core {
/// <summary>
/// 连接管理器接口
/// </summary>
public interface IConnectionManager {
event EventHandler<ProcessInfo> ProcessConnected;
event EventHandler<ProcessInfo> ProcessDisconnected;
Task<bool> ConnectToProcessAsync(string processId, string address);
Task DisconnectFromProcessAsync(string processId);
Task<bool> SendMessageAsync(string targetProcessId, IPCMessage message);
Task BroadcastMessageAsync(IPCMessage message);
ProcessInfo GetProcessInfo(string processId);
IReadOnlyList<ProcessInfo> GetAllProcesses();
}
}

View File

@@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace IpcLibrary.Core {
/// <summary>
/// 心跳管理器接口
/// </summary>
public interface IHeartbeatManager {
event EventHandler<string> ProcessTimeout;
void StartMonitoring(string processId);
void StopMonitoring(string processId);
void UpdateHeartbeat(string processId);
void SetHeartbeatInterval(TimeSpan interval);
void SetTimeoutThreshold(TimeSpan timeout);
}
}

View File

@@ -0,0 +1,27 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace IpcLibrary.Core {
/// <summary>
/// IPC客户端接口
/// </summary>
public interface IIPCClient : IDisposable {
string ProcessId { get; }
bool IsConnected { get; }
event EventHandler<ProcessInfo> ProcessConnected;
event EventHandler<ProcessInfo> ProcessDisconnected;
event EventHandler<IPCMessage> MessageReceived;
Task<bool> ConnectAsync(string serverAddress);
Task DisconnectAsync();
Task<T> CallMethodAsync<T>(string targetProcessId, string serviceName, string methodName, params object[] parameters);
Task<object> CallMethodAsync(string targetProcessId, string serviceName, string methodName, Type returnType, params object[] parameters);
Task SendNotificationAsync(string targetProcessId, string method, params object[] parameters);
Task RegisterServiceAsync<T>(string serviceName, T serviceInstance);
Task UnregisterServiceAsync(string serviceName);
}
}

View File

@@ -0,0 +1,27 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace IpcLibrary.Core {
/// <summary>
/// IPC服务器接口
/// </summary>
public interface IIPCServer : IDisposable {
bool IsRunning { get; }
IReadOnlyList<ProcessInfo> ConnectedProcesses { get; }
event EventHandler<ProcessInfo> ProcessConnected;
event EventHandler<ProcessInfo> ProcessDisconnected;
event EventHandler<IPCMessage> MessageReceived;
Task StartAsync(string address);
Task StopAsync();
Task<T> CallMethodAsync<T>(string targetProcessId, string serviceName, string methodName, params object[] parameters);
Task SendNotificationAsync(string targetProcessId, string method, params object[] parameters);
Task BroadcastNotificationAsync(string method, params object[] parameters);
Task RegisterServiceAsync<T>(string serviceName, T serviceInstance);
Task UnregisterServiceAsync(string serviceName);
}
}

View File

@@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace IpcLibrary.Core {
/// <summary>
/// 序列化器接口
/// </summary>
public interface IMessageSerializer {
byte[] Serialize(object obj);
T Deserialize<T>(byte[] data);
object Deserialize(byte[] data, Type type);
}
}

View File

@@ -0,0 +1,22 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace IpcLibrary.Core {
/// <summary>
/// 配置选项
/// </summary>
public class IPCConfiguration {
public TimeSpan HeartbeatInterval { get; set; } = TimeSpan.FromSeconds(5);
public TimeSpan ConnectionTimeout { get; set; } = TimeSpan.FromSeconds(30);
public TimeSpan MethodCallTimeout { get; set; } = TimeSpan.FromSeconds(60);
public int MaxRetryAttempts { get; set; } = 3;
public TimeSpan RetryDelay { get; set; } = TimeSpan.FromSeconds(1);
public bool EnableAutoReconnect { get; set; } = true;
public bool EnableHeartbeat { get; set; } = true;
public int MaxConcurrentConnections { get; set; } = 100;
public string LogLevel { get; set; } = "Info";
}
}

View File

@@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace IpcLibrary.Core {
/// <summary>
/// IPC消息基类
/// </summary>
[Serializable]
public class IPCMessage {
public string Id { get; set; } = Guid.NewGuid().ToString();
public MessageType Type { get; set; }
public string Method { get; set; }
public object[] Parameters { get; set; }
public object Result { get; set; }
public string Error { get; set; }
public DateTime Timestamp { get; set; } = DateTime.UtcNow;
public string SourceProcessId { get; set; }
public string TargetProcessId { get; set; }
}
}

View File

@@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace IpcLibrary.Core {
/// <summary>
/// 服务注册器接口
/// </summary>
public interface IServiceRegistry {
Task RegisterServiceAsync<T>(string serviceName, T serviceInstance);
Task UnregisterServiceAsync(string serviceName);
object GetService(string serviceName);
T GetService<T>(string serviceName);
bool IsServiceRegistered(string serviceName);
IReadOnlyList<string> GetRegisteredServices();
}
}

View File

@@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace IpcLibrary.Core {
/// <summary>
/// IPC消息类型
/// </summary>
public enum MessageType {
Request,
Response,
Notification,
Heartbeat,
Error
}
}

View File

@@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace IpcLibrary.Core {
/// <summary>
/// 方法调用请求
/// </summary>
[Serializable]
public class MethodCallRequest {
public string ServiceName { get; set; }
public string MethodName { get; set; }
public Type[] ParameterTypes { get; set; }
public object[] Parameters { get; set; }
}
}

View File

@@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace IpcLibrary.Core {
// <summary>
/// 方法调用响应
/// </summary>
[Serializable]
public class MethodCallResponse {
public object Result { get; set; }
public Exception Exception { get; set; }
public bool IsSuccess => Exception == null;
public Type ResultType { get; set; }
}
}

View File

@@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace IpcLibrary.Core {
/// <summary>
/// 进程信息
/// </summary>
public class ProcessInfo {
public string ProcessId { get; set; }
public string ProcessName { get; set; }
public ProcessStatus Status { get; set; }
public DateTime LastHeartbeat { get; set; }
public Dictionary<string, object> Metadata { get; set; } = new Dictionary<string, object>();
}
}

View File

@@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace IpcLibrary.Core {
/// <summary>
/// 进程状态
/// </summary>
public enum ProcessStatus {
Unknown,
Connected,
Disconnected,
Reconnecting,
Failed
}
}

View File

@@ -0,0 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>

View File

@@ -0,0 +1,52 @@
using IpcLibrary.Core;
using IpcLibrary.Core.Exceptions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;
using System.Threading.Tasks;
namespace IpcLibrary.Serialization {
/// <summary>
/// 二进制序列化器实现(用于复杂对象)
/// </summary>
public class BinaryMessageSerializer : IMessageSerializer {
public byte[] Serialize(object obj) {
try {
using var stream = new MemoryStream();
#pragma warning disable SYSLIB0011 // 类型或成员已过时
var formatter = new BinaryFormatter();
#pragma warning restore SYSLIB0011 // 类型或成员已过时
formatter.Serialize(stream, obj);
return stream.ToArray();
} catch (Exception ex) {
throw new IPCException($"二进制序列化失败: {ex.Message}", ex);
}
}
public T Deserialize<T>(byte[] data) {
try {
using var stream = new MemoryStream(data);
#pragma warning disable SYSLIB0011 // 类型或成员已过时
var formatter = new BinaryFormatter();
#pragma warning restore SYSLIB0011 // 类型或成员已过时
return (T)formatter.Deserialize(stream);
} catch (Exception ex) {
throw new IPCException($"二进制反序列化失败: {ex.Message}", ex);
}
}
public object Deserialize(byte[] data, Type type) {
try {
using var stream = new MemoryStream(data);
#pragma warning disable SYSLIB0011 // 类型或成员已过时
var formatter = new BinaryFormatter();
#pragma warning restore SYSLIB0011 // 类型或成员已过时
return formatter.Deserialize(stream);
} catch (Exception ex) {
throw new IPCException($"二进制反序列化失败: {ex.Message}", ex);
}
}
}
}

View File

@@ -0,0 +1,93 @@
using IpcLibrary.Core;
using IpcLibrary.Core.Exceptions;
using IPCLibrary.Serialization;
namespace IpcLibrary.Serialization {
/// <summary>
/// 复合序列化器 - 根据数据类型选择最适合的序列化方式
/// </summary>
public class CompositeMessageSerializer : IMessageSerializer {
private readonly JsonMessageSerializer _jsonSerializer;
private readonly BinaryMessageSerializer _binarySerializer;
public CompositeMessageSerializer() {
_jsonSerializer = new JsonMessageSerializer();
_binarySerializer = new BinaryMessageSerializer();
}
public byte[] Serialize(object obj) {
if (obj == null)
return new byte[0];
// 对于简单类型和已知类型使用JSON复杂类型使用二进制
if (IsJsonSerializable(obj.GetType())) {
var jsonData = _jsonSerializer.Serialize(obj);
var result = new byte[jsonData.Length + 1];
result[0] = 1; // JSON标识
Array.Copy(jsonData, 0, result, 1, jsonData.Length);
return result;
} else {
var binaryData = _binarySerializer.Serialize(obj);
var result = new byte[binaryData.Length + 1];
result[0] = 2; // Binary标识
Array.Copy(binaryData, 0, result, 1, binaryData.Length);
return result;
}
}
public T Deserialize<T>(byte[] data) {
if (data.Length == 0)
return default(T);
var serializationType = data[0];
var actualData = new byte[data.Length - 1];
Array.Copy(data, 1, actualData, 0, actualData.Length);
switch (serializationType) {
case 1: // JSON
return _jsonSerializer.Deserialize<T>(actualData);
case 2: // Binary
return _binarySerializer.Deserialize<T>(actualData);
default:
throw new IPCException($"未知的序列化类型: {serializationType}");
}
}
public object Deserialize(byte[] data, Type type) {
if (data.Length == 0)
return null;
var serializationType = data[0];
var actualData = new byte[data.Length - 1];
Array.Copy(data, 1, actualData, 0, actualData.Length);
switch (serializationType) {
case 1: // JSON
return _jsonSerializer.Deserialize(actualData, type);
case 2: // Binary
return _binarySerializer.Deserialize(actualData, type);
default:
throw new IPCException($"未知的序列化类型: {serializationType}");
}
}
private bool IsJsonSerializable(Type type) {
// 基本类型
if (type.IsPrimitive || type == typeof(string) || type == typeof(DateTime) || type == typeof(Guid))
return true;
// 数组
if (type.IsArray && IsJsonSerializable(type.GetElementType()))
return true;
// 已知的IPC消息类型
if (typeof(IPCMessage).IsAssignableFrom(type) ||
typeof(MethodCallRequest).IsAssignableFrom(type) ||
typeof(MethodCallResponse).IsAssignableFrom(type))
return true;
// 其他情况使用二进制序列化
return false;
}
}
}

View File

@@ -0,0 +1,43 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
namespace IpcLibrary.Serialization {
/// <summary>
/// Exception异常的JSON转换器
/// </summary>
public class ExceptionConverter : JsonConverter<Exception> {
public override Exception Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) {
using var doc = JsonDocument.ParseValue(ref reader);
var root = doc.RootElement;
var message = root.TryGetProperty("message", out var msgElement) ? msgElement.GetString() : "Unknown error";
var typeName = root.TryGetProperty("type", out var typeElement) ? typeElement.GetString() : null;
if (!string.IsNullOrEmpty(typeName)) {
var exceptionType = Type.GetType(typeName);
if (exceptionType != null && typeof(Exception).IsAssignableFrom(exceptionType)) {
try {
return (Exception)Activator.CreateInstance(exceptionType, message);
} catch {
// 如果创建特定异常类型失败,返回通用异常
}
}
}
return new Exception(message);
}
public override void Write(Utf8JsonWriter writer, Exception value, JsonSerializerOptions options) {
writer.WriteStartObject();
writer.WriteString("type", value.GetType().AssemblyQualifiedName);
writer.WriteString("message", value.Message);
writer.WriteString("stackTrace", value.StackTrace);
writer.WriteEndObject();
}
}
}

View File

@@ -0,0 +1,66 @@
using IpcLibrary.Core;
using IpcLibrary.Core.Exceptions;
using IpcLibrary.Serialization;
using System;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace IPCLibrary.Serialization {
/// <summary>
/// JSON序列化器实现
/// </summary>
public class JsonMessageSerializer : IMessageSerializer {
private readonly JsonSerializerOptions _options;
public JsonMessageSerializer() {
_options = new JsonSerializerOptions {
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
WriteIndented = false,
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
Converters =
{
new JsonStringEnumConverter(),
new TypeConverter(),
new ExceptionConverter()
}
};
}
public byte[] Serialize(object obj) {
try {
var json = JsonSerializer.Serialize(obj, obj.GetType(), _options);
return System.Text.Encoding.UTF8.GetBytes(json);
} catch (Exception ex) {
throw new IPCException($"序列化失败: {ex.Message}", ex);
}
}
public T Deserialize<T>(byte[] data) {
try {
var json = System.Text.Encoding.UTF8.GetString(data);
return JsonSerializer.Deserialize<T>(json, _options);
} catch (Exception ex) {
throw new IPCException($"反序列化失败: {ex.Message}", ex);
}
}
public object Deserialize(byte[] data, Type type) {
try {
var json = System.Text.Encoding.UTF8.GetString(data);
return JsonSerializer.Deserialize(json, type, _options);
} catch (Exception ex) {
throw new IPCException($"反序列化失败: {ex.Message}", ex);
}
}
}
}

View File

@@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
namespace IpcLibrary.Serialization {
/// <summary>
/// Type类型的JSON转换器
/// </summary>
public class TypeConverter : JsonConverter<Type> {
public override Type Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) {
var typeName = reader.GetString();
return string.IsNullOrEmpty(typeName) ? null : Type.GetType(typeName);
}
public override void Write(Utf8JsonWriter writer, Type value, JsonSerializerOptions options) {
writer.WriteStringValue(value?.AssemblyQualifiedName);
}
}
}

View File

@@ -0,0 +1,258 @@
using IpcLibrary.Connection;
using IpcLibrary.Core;
using IpcLibrary.Core.Exceptions;
using IpcLibrary.Serialization;
using IpcLibrary.Services;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace IpcLibrary.Server {
/// <summary>
/// IPC服务器实现
/// </summary>
public class IPCServer : IIPCServer {
private readonly NamedPipeConnectionManager _connectionManager;
private readonly IServiceRegistry _serviceRegistry;
private readonly HeartbeatManager _heartbeatManager;
private readonly MessageRouter _messageRouter;
private readonly IPCConfiguration _configuration;
private readonly string _serverId;
private bool _disposed;
public bool IsRunning { get; private set; }
public IReadOnlyList<ProcessInfo> ConnectedProcesses => _connectionManager.GetAllProcesses();
public DateTime StartTime { get; internal set; }
public event EventHandler<ProcessInfo> ProcessConnected;
public event EventHandler<ProcessInfo> ProcessDisconnected;
public event EventHandler<IPCMessage> MessageReceived;
public IPCServer(IPCConfiguration configuration = null) {
_configuration = configuration ?? new IPCConfiguration();
_serverId = $"Server_{Environment.ProcessId}_{Guid.NewGuid():N}";
var serializer = new CompositeMessageSerializer();
_connectionManager = new NamedPipeConnectionManager(serializer, _configuration, "IPCPipe");
_serviceRegistry = new ServiceRegistry();
_heartbeatManager = new HeartbeatManager();
var methodCallHandler = new MethodCallHandler(_serviceRegistry);
_messageRouter = new MessageRouter(methodCallHandler);
InitializeEventHandlers();
StartTime = DateTime.UtcNow;
}
public async Task StartAsync(string address) {
if (IsRunning)
return;
try {
Console.WriteLine($"IPC服务器已启动地址: {address}");
if (_connectionManager is NamedPipeConnectionManager namedPipeManager) {
await namedPipeManager.StartServerAsync();
}
IsRunning = true;
if (_configuration.EnableHeartbeat) {
_heartbeatManager.SetHeartbeatInterval(_configuration.HeartbeatInterval);
_heartbeatManager.SetTimeoutThreshold(_configuration.ConnectionTimeout);
}
Console.WriteLine($"IPC服务器已启动地址: {address}");
} catch (Exception ex) {
throw new ConnectionException($"启动服务器失败: {ex.Message}", ex);
}
}
public async Task StopAsync() {
if (!IsRunning)
return;
try {
// 通知所有连接的进程服务器即将关闭
var shutdownMessage = new IPCMessage {
Type = MessageType.Notification,
Method = "ServerShutdown",
SourceProcessId = _serverId
};
await _connectionManager.BroadcastMessageAsync(shutdownMessage);
// 断开所有连接
var processes = ConnectedProcesses.ToList();
foreach (var process in processes) {
await _connectionManager.DisconnectFromProcessAsync(process.ProcessId);
}
IsRunning = false;
Console.WriteLine("IPC服务器已停止");
} catch (Exception ex) {
throw new ConnectionException($"停止服务器失败: {ex.Message}", ex);
}
}
public async Task<T> CallMethodAsync<T>(string targetProcessId, string serviceName, string methodName, params object[] parameters) {
var result = await CallMethodAsync(targetProcessId, serviceName, methodName, typeof(T), parameters);
return result is T typedResult ? typedResult : default(T);
}
public async Task<object> CallMethodAsync(string targetProcessId, string serviceName, string methodName, Type returnType, params object[] parameters) {
if (!IsRunning)
throw new InvalidOperationException("服务器未运行");
try {
var request = new MethodCallRequest {
ServiceName = serviceName,
MethodName = methodName,
Parameters = parameters,
ParameterTypes = parameters != null ? Array.ConvertAll(parameters, p => p?.GetType()) : new Type[0]
};
var message = new IPCMessage {
Type = MessageType.Request,
Method = "CallMethod",
Parameters = new object[] { request },
SourceProcessId = _serverId,
TargetProcessId = targetProcessId
};
var response = await _messageRouter.SendRequestAsync(
message,
msg => _connectionManager.SendMessageAsync(targetProcessId, msg),
_configuration.MethodCallTimeout);
if (response.Type == MessageType.Error) {
throw new MethodCallException(response.Error);
}
if (response.Result is MethodCallResponse methodResponse) {
if (!methodResponse.IsSuccess) {
throw new MethodCallException("方法调用失败", methodResponse.Exception);
}
return methodResponse.Result;
}
return response.Result;
} catch (Exception ex) when (!(ex is IPCException)) {
throw new MethodCallException($"调用方法失败: {ex.Message}", ex);
}
}
public async Task SendNotificationAsync(string targetProcessId, string method, params object[] parameters) {
if (!IsRunning)
throw new InvalidOperationException("服务器未运行");
var message = new IPCMessage {
Type = MessageType.Notification,
Method = method,
Parameters = parameters,
SourceProcessId = _serverId,
TargetProcessId = targetProcessId
};
await _connectionManager.SendMessageAsync(targetProcessId, message);
}
public async Task BroadcastNotificationAsync(string method, params object[] parameters) {
if (!IsRunning)
throw new InvalidOperationException("服务器未运行");
var message = new IPCMessage {
Type = MessageType.Notification,
Method = method,
Parameters = parameters,
SourceProcessId = _serverId
};
await _connectionManager.BroadcastMessageAsync(message);
}
public async Task RegisterServiceAsync<T>(string serviceName, T serviceInstance) {
await _serviceRegistry.RegisterServiceAsync(serviceName, serviceInstance);
}
public async Task UnregisterServiceAsync(string serviceName) {
await _serviceRegistry.UnregisterServiceAsync(serviceName);
}
private void InitializeEventHandlers() {
_connectionManager.ProcessConnected += OnProcessConnected;
_connectionManager.ProcessDisconnected += OnProcessDisconnected;
_heartbeatManager.ProcessTimeout += async (sender, processId) => {
Console.WriteLine($"进程 {processId} 心跳超时,断开连接");
await _connectionManager.DisconnectFromProcessAsync(processId);
};
}
private void OnProcessConnected(object sender, ProcessInfo processInfo) {
Console.WriteLine($"进程已连接: {processInfo.ProcessId}");
if (_configuration.EnableHeartbeat) {
_heartbeatManager.StartMonitoring(processInfo.ProcessId);
}
ProcessConnected?.Invoke(this, processInfo);
}
private void OnProcessDisconnected(object sender, ProcessInfo processInfo) {
Console.WriteLine($"进程已断开: {processInfo.ProcessId}");
_heartbeatManager.StopMonitoring(processInfo.ProcessId);
ProcessDisconnected?.Invoke(this, processInfo);
}
private async Task HandleIncomingMessage(IPCMessage message) {
try {
// 更新心跳
if (_configuration.EnableHeartbeat && message.Type == MessageType.Heartbeat) {
_heartbeatManager.UpdateHeartbeat(message.SourceProcessId);
return;
}
// 处理消息
var response = await _messageRouter.HandleMessageAsync(message);
if (response != null) {
await _connectionManager.SendMessageAsync(message.SourceProcessId, response);
}
MessageReceived?.Invoke(this, message);
} catch (Exception ex) {
Console.WriteLine($"处理消息时发生错误: {ex.Message}");
// 发送错误响应
var errorResponse = new IPCMessage {
Id = message.Id,
Type = MessageType.Error,
Error = ex.Message,
SourceProcessId = _serverId,
TargetProcessId = message.SourceProcessId
};
await _connectionManager.SendMessageAsync(message.SourceProcessId, errorResponse);
}
}
public void Dispose() {
if (_disposed)
return;
_disposed = true;
try {
StopAsync().Wait(TimeSpan.FromSeconds(10));
} catch (Exception ex) {
Console.WriteLine($"释放资源时发生错误: {ex.Message}");
}
_connectionManager?.Dispose();
_heartbeatManager?.Dispose();
}
}
}

View File

@@ -0,0 +1,93 @@
using IpcLibrary.Core;
using IpcLibrary.Core.Exceptions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace IpcLibrary.Server {
/// <summary>
/// 服务器扩展方法
/// </summary>
public static class IPCServerExtensions {
/// <summary>
/// 调用无返回值的方法
/// </summary>
public static async Task CallMethodAsync(this IIPCServer server, string targetProcessId, string serviceName, string methodName, params object[] parameters) {
await server.CallMethodAsync<object>(targetProcessId, serviceName, methodName, parameters);
}
/// <summary>
/// 注册多个服务
/// </summary>
public static async Task RegisterServicesAsync(this IIPCServer server, Dictionary<string, object> services) {
foreach (var kvp in services) {
await server.RegisterServiceAsync(kvp.Key, kvp.Value);
}
}
/// <summary>
/// 获取在线进程数量
/// </summary>
public static int GetOnlineProcessCount(this IIPCServer server) {
return server.ConnectedProcesses.Count(p => p.Status == ProcessStatus.Connected);
}
/// <summary>
/// 获取指定状态的进程
/// </summary>
public static IEnumerable<ProcessInfo> GetProcessesByStatus(this IIPCServer server, ProcessStatus status) {
return server.ConnectedProcesses.Where(p => p.Status == status);
}
/// <summary>
/// 向指定状态的所有进程发送通知
/// </summary>
public static async Task BroadcastToStatusAsync(this IIPCServer server, ProcessStatus status, string method, params object[] parameters) {
var processes = server.GetProcessesByStatus(status);
var tasks = processes.Select(p => server.SendNotificationAsync(p.ProcessId, method, parameters));
await Task.WhenAll(tasks);
}
/// <summary>
/// 使用重试机制调用方法
/// </summary>
public static async Task<T> CallMethodWithRetryAsync<T>(this IIPCServer server, string targetProcessId, string serviceName, string methodName, int maxRetries = 3, TimeSpan? retryDelay = null, params object[] parameters) {
var delay = retryDelay ?? TimeSpan.FromSeconds(1);
Exception lastException = null;
for (int attempt = 0; attempt <= maxRetries; attempt++) {
try {
return await server.CallMethodAsync<T>(targetProcessId, serviceName, methodName, parameters);
} catch (Exception ex) {
lastException = ex;
if (attempt < maxRetries) {
await Task.Delay(delay);
}
}
}
throw new MethodCallException($"方法调用在 {maxRetries + 1} 次尝试后仍然失败", lastException);
}
/// <summary>
/// 等待指定数量的进程连接
/// </summary>
public static async Task<bool> WaitForProcessesAsync(this IIPCServer server, int expectedCount, TimeSpan timeout) {
var startTime = DateTime.UtcNow;
while (DateTime.UtcNow - startTime < timeout) {
if (server.GetOnlineProcessCount() >= expectedCount) {
return true;
}
await Task.Delay(100);
}
return false;
}
}
}

View File

@@ -0,0 +1,37 @@
using IpcLibrary.Core;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace IpcLibrary.Server {
/// <summary>
/// IPC服务器工厂
/// </summary>
public static class IPCServerFactory {
/// <summary>
/// 创建默认配置的服务器
/// </summary>
public static IIPCServer CreateServer() {
return new IPCServer();
}
/// <summary>
/// 创建自定义配置的服务器
/// </summary>
public static IIPCServer CreateServer(IPCConfiguration configuration) {
return new IPCServer(configuration);
}
/// <summary>
/// 创建并启动服务器
/// </summary>
public static async Task<IIPCServer> CreateAndStartAsync(string address, IPCConfiguration configuration = null) {
var server = new IPCServer(configuration);
await server.StartAsync(address);
return server;
}
}
}

View File

@@ -0,0 +1,90 @@
using IpcLibrary.Core;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace IpcLibrary.Server {
/// <summary>
/// 服务器状态监控器
/// </summary>
public class ServerMonitor : IDisposable {
private readonly IPCServer _server;
private readonly System.Timers.Timer _monitorTimer;
private readonly Dictionary<string, DateTime> _processLastSeen;
private bool _disposed;
public event EventHandler<ServerStatistics> StatisticsUpdated;
public ServerMonitor(IPCServer server, TimeSpan monitorInterval) {
_server = server ?? throw new ArgumentNullException(nameof(server));
_processLastSeen = new Dictionary<string, DateTime>();
_monitorTimer = new System.Timers.Timer(monitorInterval.TotalMilliseconds);
_monitorTimer.Elapsed += OnMonitorTimer;
_monitorTimer.Start();
_server.ProcessConnected += OnProcessConnected;
_server.ProcessDisconnected += OnProcessDisconnected;
}
private void OnProcessConnected(object sender, ProcessInfo processInfo) {
lock (_processLastSeen) {
_processLastSeen[processInfo.ProcessId] = DateTime.UtcNow;
}
}
private void OnProcessDisconnected(object sender, ProcessInfo processInfo) {
lock (_processLastSeen) {
_processLastSeen.Remove(processInfo.ProcessId);
}
}
private void OnMonitorTimer(object sender, System.Timers.ElapsedEventArgs e) {
try {
var statistics = GatherStatistics();
StatisticsUpdated?.Invoke(this, statistics);
} catch (Exception ex) {
Console.WriteLine($"收集统计信息时发生错误: {ex.Message}");
}
}
private ServerStatistics GatherStatistics() {
var connectedProcesses = _server.ConnectedProcesses;
var now = DateTime.UtcNow;
return new ServerStatistics {
Timestamp = now,
TotalConnectedProcesses = connectedProcesses.Count,
OnlineProcesses = connectedProcesses.Count(p => p.Status == ProcessStatus.Connected),
DisconnectedProcesses = connectedProcesses.Count(p => p.Status == ProcessStatus.Disconnected),
ReconnectingProcesses = connectedProcesses.Count(p => p.Status == ProcessStatus.Reconnecting),
AverageHeartbeatDelay = CalculateAverageHeartbeatDelay(connectedProcesses, now),
ServerUptime = now - _server.StartTime // 需要在IPCServer中添加StartTime属性
};
}
private TimeSpan CalculateAverageHeartbeatDelay(IReadOnlyList<ProcessInfo> processes, DateTime now) {
var delays = processes
.Where(p => p.Status == ProcessStatus.Connected)
.Select(p => now - p.LastHeartbeat)
.Where(delay => delay.TotalSeconds >= 0);
if (!delays.Any())
return TimeSpan.Zero;
var averageTicks = delays.Select(d => d.Ticks).Average();
return new TimeSpan((long)averageTicks);
}
public void Dispose() {
if (_disposed)
return;
_disposed = true;
_monitorTimer?.Stop();
_monitorTimer?.Dispose();
}
}
}

View File

@@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace IpcLibrary.Server {
/// <summary>
/// 服务器统计信息
/// </summary>
public class ServerStatistics {
public DateTime Timestamp { get; set; }
public int TotalConnectedProcesses { get; set; }
public int OnlineProcesses { get; set; }
public int DisconnectedProcesses { get; set; }
public int ReconnectingProcesses { get; set; }
public TimeSpan AverageHeartbeatDelay { get; set; }
public TimeSpan ServerUptime { get; set; }
}
}

View File

@@ -0,0 +1,100 @@
using IpcLibrary.Core;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace IpcLibrary.Services {
/// <summary>
/// 心跳管理器实现
/// </summary>
public class HeartbeatManager : IHeartbeatManager, IDisposable {
private readonly ConcurrentDictionary<string, ProcessHeartbeat> _heartbeats;
private readonly Timer _monitorTimer;
private readonly object _lock = new object();
private TimeSpan _heartbeatInterval = TimeSpan.FromSeconds(5);
private TimeSpan _timeoutThreshold = TimeSpan.FromSeconds(15);
private bool _disposed;
public event EventHandler<string> ProcessTimeout;
public HeartbeatManager() {
_heartbeats = new ConcurrentDictionary<string, ProcessHeartbeat>();
_monitorTimer = new Timer(MonitorHeartbeats, null, _heartbeatInterval, _heartbeatInterval);
}
public void StartMonitoring(string processId) {
if (string.IsNullOrEmpty(processId))
throw new ArgumentException("进程ID不能为空", nameof(processId));
var heartbeat = new ProcessHeartbeat {
ProcessId = processId,
LastHeartbeat = DateTime.UtcNow,
IsActive = true
};
_heartbeats.AddOrUpdate(processId, heartbeat, (key, old) => heartbeat);
}
public void StopMonitoring(string processId) {
if (_heartbeats.TryGetValue(processId, out var heartbeat)) {
heartbeat.IsActive = false;
_heartbeats.TryRemove(processId, out _);
}
}
public void UpdateHeartbeat(string processId) {
if (_heartbeats.TryGetValue(processId, out var heartbeat)) {
heartbeat.LastHeartbeat = DateTime.UtcNow;
}
}
public void SetHeartbeatInterval(TimeSpan interval) {
if (interval <= TimeSpan.Zero)
throw new ArgumentException("心跳间隔必须大于零", nameof(interval));
lock (_lock) {
_heartbeatInterval = interval;
_monitorTimer?.Change(interval, interval);
}
}
public void SetTimeoutThreshold(TimeSpan timeout) {
if (timeout <= TimeSpan.Zero)
throw new ArgumentException("超时阈值必须大于零", nameof(timeout));
_timeoutThreshold = timeout;
}
private void MonitorHeartbeats(object state) {
if (_disposed)
return;
var now = DateTime.UtcNow;
var timedOutProcesses = new List<string>();
foreach (var kvp in _heartbeats) {
var heartbeat = kvp.Value;
if (heartbeat.IsActive && now - heartbeat.LastHeartbeat > _timeoutThreshold) {
timedOutProcesses.Add(kvp.Key);
heartbeat.IsActive = false;
}
}
foreach (var processId in timedOutProcesses) {
ProcessTimeout?.Invoke(this, processId);
}
}
public void Dispose() {
if (_disposed)
return;
_disposed = true;
_monitorTimer?.Dispose();
_heartbeats.Clear();
}
}
}

View File

@@ -0,0 +1,108 @@
using IpcLibrary.Core;
using IpcLibrary.Core.Exceptions;
using System;
using System.Collections.Concurrent;
namespace IpcLibrary.Services {
/// <summary>
/// 消息路由器
/// </summary>
public class MessageRouter {
private readonly ConcurrentDictionary<string, TaskCompletionSource<IPCMessage>> _pendingRequests;
private readonly MethodCallHandler _methodCallHandler;
private readonly Timer _timeoutTimer;
public MessageRouter(MethodCallHandler methodCallHandler) {
_pendingRequests = new ConcurrentDictionary<string, TaskCompletionSource<IPCMessage>>();
_methodCallHandler = methodCallHandler ?? throw new ArgumentNullException(nameof(methodCallHandler));
_timeoutTimer = new Timer(CheckTimeouts, null, TimeSpan.FromSeconds(10), TimeSpan.FromSeconds(10));
}
public async Task<IPCMessage> SendRequestAsync(IPCMessage request, Func<IPCMessage, Task<bool>> sendAction, TimeSpan timeout) {
var tcs = new TaskCompletionSource<IPCMessage>();
_pendingRequests[request.Id] = tcs;
try {
if (!await sendAction(request)) {
_pendingRequests.TryRemove(request.Id, out _);
throw new ConnectionException("发送请求失败");
}
using var timeoutCts = new CancellationTokenSource(timeout);
timeoutCts.Token.Register(() => tcs.TrySetException(new Core.Exceptions.TimeoutException("请求超时")));
return await tcs.Task;
} finally {
_pendingRequests.TryRemove(request.Id, out _);
}
}
public async Task<IPCMessage> HandleMessageAsync(IPCMessage message) {
switch (message.Type) {
case MessageType.Request:
return await HandleRequestAsync(message);
case MessageType.Response:
HandleResponse(message);
return null;
case MessageType.Notification:
await HandleNotificationAsync(message);
return null;
default:
return null;
}
}
private async Task<IPCMessage> HandleRequestAsync(IPCMessage request) {
try {
if (request.Parameters?.Length > 0 && request.Parameters[0] is MethodCallRequest methodCall) {
var response = await _methodCallHandler.HandleMethodCallAsync(methodCall);
return new IPCMessage {
Id = Guid.NewGuid().ToString(),
Type = MessageType.Response,
Result = response,
SourceProcessId = request.TargetProcessId,
TargetProcessId = request.SourceProcessId
};
}
} catch (Exception ex) {
return new IPCMessage {
Id = Guid.NewGuid().ToString(),
Type = MessageType.Error,
Error = ex.Message,
SourceProcessId = request.TargetProcessId,
TargetProcessId = request.SourceProcessId
};
}
return null;
}
private void HandleResponse(IPCMessage response) {
if (_pendingRequests.TryGetValue(response.Id, out var tcs)) {
tcs.SetResult(response);
}
}
private async Task HandleNotificationAsync(IPCMessage notification) {
// 通知类型消息的处理逻辑
await Task.CompletedTask;
}
private void CheckTimeouts(object state) {
var expiredRequests = _pendingRequests.Where(kvp =>
DateTime.UtcNow - DateTime.MinValue > TimeSpan.FromMinutes(5)) // 简化的超时检查
.ToList();
foreach (var kvp in expiredRequests) {
if (_pendingRequests.TryRemove(kvp.Key, out var tcs)) {
tcs.TrySetException(new Core.Exceptions.TimeoutException("请求超时"));
}
}
}
}
}

View File

@@ -0,0 +1,84 @@
using IpcLibrary.Core;
using IpcLibrary.Core.Exceptions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace IpcLibrary.Services {
/// <summary>
/// 方法调用处理器
/// </summary>
public class MethodCallHandler {
private readonly IServiceRegistry _serviceRegistry;
public MethodCallHandler(IServiceRegistry serviceRegistry) {
_serviceRegistry = serviceRegistry ?? throw new ArgumentNullException(nameof(serviceRegistry));
}
public async Task<MethodCallResponse> HandleMethodCallAsync(MethodCallRequest request) {
try {
var service = _serviceRegistry.GetService(request.ServiceName);
if (service == null) {
return new MethodCallResponse {
Exception = new IPCException($"服务 '{request.ServiceName}' 未找到")
};
}
var serviceType = service.GetType();
MethodInfo method = null;
// 查找匹配的方法
if (request.ParameterTypes != null && request.ParameterTypes.Length > 0) {
method = serviceType.GetMethod(request.MethodName, request.ParameterTypes);
} else {
// 如果没有提供参数类型,尝试按名称和参数数量匹配
var methods = serviceType.GetMethods().Where(m => m.Name == request.MethodName);
var paramCount = request.Parameters?.Length ?? 0;
method = methods.FirstOrDefault(m => m.GetParameters().Length == paramCount);
}
if (method == null) {
return new MethodCallResponse {
Exception = new IPCException($"方法 '{request.MethodName}' 在服务 '{request.ServiceName}' 中未找到")
};
}
// 调用方法
object result;
if (method.ReturnType == typeof(Task)) {
// 异步方法无返回值
var task = (Task)method.Invoke(service, request.Parameters);
await task;
result = null;
} else if (method.ReturnType.IsGenericType && method.ReturnType.GetGenericTypeDefinition() == typeof(Task<>)) {
// 异步方法有返回值
var task = (Task)method.Invoke(service, request.Parameters);
await task;
var resultProperty = task.GetType().GetProperty("Result");
result = resultProperty?.GetValue(task);
} else {
// 同步方法
result = method.Invoke(service, request.Parameters);
}
return new MethodCallResponse {
Result = result,
ResultType = method.ReturnType
};
} catch (TargetInvocationException ex) {
return new MethodCallResponse {
Exception = ex.InnerException ?? ex
};
} catch (Exception ex) {
return new MethodCallResponse {
Exception = ex
};
}
}
}
}

View File

@@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace IpcLibrary.Services {
/// <summary>
/// 进程心跳信息
/// </summary>
internal class ProcessHeartbeat {
public string ProcessId { get; set; }
public DateTime LastHeartbeat { get; set; }
public bool IsActive { get; set; }
}
}

View File

@@ -0,0 +1,14 @@
using System.Reflection;
namespace IpcLibrary.Services {
/// <summary>
/// 服务信息
/// </summary>
public class ServiceInfo {
public string ServiceName { get; set; }
public object ServiceInstance { get; set; }
public Type ServiceType { get; set; }
public DateTime RegisterTime { get; set; }
public Dictionary<string, MethodInfo> Methods { get; set; }
}
}

View File

@@ -0,0 +1,94 @@
using IpcLibrary.Core;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace IpcLibrary.Services {
/// <summary>
/// 服务注册器实现
/// </summary>
public class ServiceRegistry : IServiceRegistry {
private readonly ConcurrentDictionary<string, ServiceInfo> _services;
private readonly object _lock = new object();
public ServiceRegistry() {
_services = new ConcurrentDictionary<string, ServiceInfo>();
}
public Task RegisterServiceAsync<T>(string serviceName, T serviceInstance) {
if (string.IsNullOrEmpty(serviceName))
throw new ArgumentException("服务名称不能为空", nameof(serviceName));
if (serviceInstance == null)
throw new ArgumentNullException(nameof(serviceInstance));
var serviceInfo = new ServiceInfo {
ServiceName = serviceName,
ServiceInstance = serviceInstance,
ServiceType = typeof(T),
RegisterTime = DateTime.UtcNow,
Methods = GetServiceMethods(typeof(T))
};
_services.AddOrUpdate(serviceName, serviceInfo, (key, old) => serviceInfo);
return Task.CompletedTask;
}
public Task UnregisterServiceAsync(string serviceName) {
if (string.IsNullOrEmpty(serviceName))
throw new ArgumentException("服务名称不能为空", nameof(serviceName));
_services.TryRemove(serviceName, out _);
return Task.CompletedTask;
}
public object GetService(string serviceName) {
return _services.TryGetValue(serviceName, out var serviceInfo)
? serviceInfo.ServiceInstance
: null;
}
public T GetService<T>(string serviceName) {
var service = GetService(serviceName);
return service is T typedService ? typedService : default(T);
}
public bool IsServiceRegistered(string serviceName) {
return _services.ContainsKey(serviceName);
}
public IReadOnlyList<string> GetRegisteredServices() {
return _services.Keys.ToList();
}
public ServiceInfo GetServiceInfo(string serviceName) {
return _services.TryGetValue(serviceName, out var serviceInfo) ? serviceInfo : null;
}
private Dictionary<string, MethodInfo> GetServiceMethods(Type serviceType) {
var methods = new Dictionary<string, MethodInfo>();
foreach (var method in serviceType.GetMethods(BindingFlags.Public | BindingFlags.Instance)) {
// 排除Object的基本方法
if (method.DeclaringType == typeof(object))
continue;
var key = GetMethodKey(method);
methods[key] = method;
}
return methods;
}
private string GetMethodKey(MethodInfo method) {
var parameters = method.GetParameters();
var paramTypes = string.Join(",", parameters.Select(p => p.ParameterType.FullName));
return $"{method.Name}({paramTypes})";
}
}
}