From 219118d3ea20745b1ba760bb81d1c2778e616301 Mon Sep 17 00:00:00 2001 From: root <295172551@qq.com> Date: Tue, 22 Jul 2025 23:32:47 +0800 Subject: [PATCH] =?UTF-8?q?=E9=87=8D=E6=9E=84WebSocket=E6=B6=88=E6=81=AF?= =?UTF-8?q?=E7=AE=A1=E7=90=86=E5=99=A8=EF=BC=9A=E5=AE=8C=E5=96=84PublicMet?= =?UTF-8?q?hods.cs=E6=96=87=E6=A1=A3=E5=92=8C=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CoreAgent.ProtocolClient.csproj | 20 + .../Docs/WebSocketMgr/Constructor.md | 64 +++ .../Docs/WebSocketMgr/Dispose.md | 113 +++++ .../Docs/WebSocketMgr/PrivateMethods.md | 400 +++++++++++++++ .../Docs/WebSocketMgr/PublicMethods.md | 468 +++++++++++++++++ .../WebSocketMgr/WebSocketMessageManager.md | 465 +++++++++++++++++ .../Docs/WebSocketMgr/结构层次.md | 35 ++ CoreAgent.ProtocolClient/Enums/ClientState.cs | 19 + CoreAgent.ProtocolClient/Enums/LayerDir.cs | 17 + .../Enums/LogChannelId.cs | 79 +++ .../Enums/ProtocolLayer.cs | 61 +++ .../HandlerCleanupEventArgs.cs | 39 ++ .../LogGetIdChangedEventArgs.cs | 35 ++ .../Managers/MessageIdManager.cs | 479 ++++++++++++++++++ .../Managers/WebSocketMgr/Constructor.cs | 59 +++ .../Managers/WebSocketMgr/Dispose.cs | 108 ++++ .../Managers/WebSocketMgr/PrivateMethods.cs | 394 ++++++++++++++ .../Managers/WebSocketMgr/PublicMethods.cs | 462 +++++++++++++++++ .../WebSocketMgr/WebSocketMessageManager.cs | 459 +++++++++++++++++ .../Managers/WebSocketMgr/结构层次.md | 35 ++ CoreAgent.ProtocolClient/Models/CellConfig.cs | 222 ++++++++ .../Models/ClientConfig.cs | 112 ++++ .../Models/LogLayerHelp.cs | 60 +++ .../Models/MessageHandler.cs | 36 ++ .../Models/ProtocolCaps.cs | 111 ++++ .../Models/ProtocolLog.cs | 212 ++++++++ .../Models/ProtocolLogDetail.cs | 117 +++++ .../Models/ProtocolLogJson.cs | 168 ++++++ .../Models/TmsiMatchProcessor.cs | 324 ++++++++++++ .../Models/TmsiMatchResult.cs | 56 ++ CoreAgent.ProtocolClient/Models/UeInfo.cs | 62 +++ CoreAgent.ProtocolClient/modify.md | 153 ++++++ CoreAgent.sln | 9 + 33 files changed, 5453 insertions(+) create mode 100644 CoreAgent.ProtocolClient/CoreAgent.ProtocolClient.csproj create mode 100644 CoreAgent.ProtocolClient/Docs/WebSocketMgr/Constructor.md create mode 100644 CoreAgent.ProtocolClient/Docs/WebSocketMgr/Dispose.md create mode 100644 CoreAgent.ProtocolClient/Docs/WebSocketMgr/PrivateMethods.md create mode 100644 CoreAgent.ProtocolClient/Docs/WebSocketMgr/PublicMethods.md create mode 100644 CoreAgent.ProtocolClient/Docs/WebSocketMgr/WebSocketMessageManager.md create mode 100644 CoreAgent.ProtocolClient/Docs/WebSocketMgr/结构层次.md create mode 100644 CoreAgent.ProtocolClient/Enums/ClientState.cs create mode 100644 CoreAgent.ProtocolClient/Enums/LayerDir.cs create mode 100644 CoreAgent.ProtocolClient/Enums/LogChannelId.cs create mode 100644 CoreAgent.ProtocolClient/Enums/ProtocolLayer.cs create mode 100644 CoreAgent.ProtocolClient/HandlerEventArgs/HandlerCleanupEventArgs.cs create mode 100644 CoreAgent.ProtocolClient/HandlerEventArgs/LogGetIdChangedEventArgs.cs create mode 100644 CoreAgent.ProtocolClient/Managers/MessageIdManager.cs create mode 100644 CoreAgent.ProtocolClient/Managers/WebSocketMgr/Constructor.cs create mode 100644 CoreAgent.ProtocolClient/Managers/WebSocketMgr/Dispose.cs create mode 100644 CoreAgent.ProtocolClient/Managers/WebSocketMgr/PrivateMethods.cs create mode 100644 CoreAgent.ProtocolClient/Managers/WebSocketMgr/PublicMethods.cs create mode 100644 CoreAgent.ProtocolClient/Managers/WebSocketMgr/WebSocketMessageManager.cs create mode 100644 CoreAgent.ProtocolClient/Managers/WebSocketMgr/结构层次.md create mode 100644 CoreAgent.ProtocolClient/Models/CellConfig.cs create mode 100644 CoreAgent.ProtocolClient/Models/ClientConfig.cs create mode 100644 CoreAgent.ProtocolClient/Models/LogLayerHelp.cs create mode 100644 CoreAgent.ProtocolClient/Models/MessageHandler.cs create mode 100644 CoreAgent.ProtocolClient/Models/ProtocolCaps.cs create mode 100644 CoreAgent.ProtocolClient/Models/ProtocolLog.cs create mode 100644 CoreAgent.ProtocolClient/Models/ProtocolLogDetail.cs create mode 100644 CoreAgent.ProtocolClient/Models/ProtocolLogJson.cs create mode 100644 CoreAgent.ProtocolClient/Models/TmsiMatchProcessor.cs create mode 100644 CoreAgent.ProtocolClient/Models/TmsiMatchResult.cs create mode 100644 CoreAgent.ProtocolClient/Models/UeInfo.cs create mode 100644 CoreAgent.ProtocolClient/modify.md diff --git a/CoreAgent.ProtocolClient/CoreAgent.ProtocolClient.csproj b/CoreAgent.ProtocolClient/CoreAgent.ProtocolClient.csproj new file mode 100644 index 0000000..d949a3a --- /dev/null +++ b/CoreAgent.ProtocolClient/CoreAgent.ProtocolClient.csproj @@ -0,0 +1,20 @@ + + + + net8.0 + enable + enable + + + + + + + + + + + + + + diff --git a/CoreAgent.ProtocolClient/Docs/WebSocketMgr/Constructor.md b/CoreAgent.ProtocolClient/Docs/WebSocketMgr/Constructor.md new file mode 100644 index 0000000..113aadb --- /dev/null +++ b/CoreAgent.ProtocolClient/Docs/WebSocketMgr/Constructor.md @@ -0,0 +1,64 @@ +# Constructor.cs (自动转换为Markdown) + +```csharp +// 以下内容为原始C#代码,含详细注释 +// 文件原路径:Managers/WebSocketMgr/Constructor.cs +using Microsoft.Extensions.Logging; +using Newtonsoft.Json.Linq; +using Newtonsoft.Json; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using WebSocket4Net; + +namespace CoreAgent.ProtocolClient.Managers.WebSocketMgr +{ + public partial class WebSocketMessageManager + { + #region 构造函数 + + /// + /// 构造函数 - 对应LTEClientWebSocket构造函数中的WebSocket相关初始化 + /// + /// 初始化说明: + /// 1. 创建MessageIdManager替代原始的_messageId和_logGetId字段 + /// 2. 创建消息队列_messageFifo,保持与原始实现一致 + /// 3. 不初始化WebSocket实例,在Connect方法中创建 + /// 4. 移除_sentMessages和_receivedMessages的初始化 + /// + /// 对应关系: + /// - 参数clientName:对应LTEClientWebSocket构造函数中的config.Name + /// - 参数logger:对应LTEClientWebSocket构造函数中的logger参数 + /// - _messageIdManager:替代原始的_messageId和_logGetId字段 + /// - _messageFifo:对应原始的_messageFifo初始化 + /// - _disposed:对应原始的_disposed初始化 + /// - 日志记录:对应原始的构造函数日志记录 + /// + /// 重构改进: + /// - 参数验证:增加了对clientName和logger的null检查 + /// - 职责分离:专注于WebSocket相关初始化 + /// - 功能增强:通过MessageIdManager提供更好的消息ID管理 + /// - 移除冗余:移除了消息缓存相关的初始化 + /// + /// 客户端名称,对应LTEClientWebSocket._config.Name + /// 日志记录器,对应LTEClientWebSocket._logger + public WebSocketMessageManager(string clientName, ILogger logger) + { + _clientName = clientName ?? throw new ArgumentNullException(nameof(clientName)); + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + + // 创建MessageIdManager,替代原始的_messageId和_logGetId字段 + _messageIdManager = new MessageIdManager(clientName, logger); + + // 创建消息队列,使用BlockingCollection优化线程安全性和性能 + _messageFifo = new BlockingCollection(); + + _logger.LogInformation($"[{_clientName}] 创建WebSocket消息管理器"); + } + + #endregion + } +} \ No newline at end of file diff --git a/CoreAgent.ProtocolClient/Docs/WebSocketMgr/Dispose.md b/CoreAgent.ProtocolClient/Docs/WebSocketMgr/Dispose.md new file mode 100644 index 0000000..9ad4f19 --- /dev/null +++ b/CoreAgent.ProtocolClient/Docs/WebSocketMgr/Dispose.md @@ -0,0 +1,113 @@ +# Dispose.cs (自动转换为Markdown) + +```csharp +// 以下内容为原始C#代码,含详细注释 +// 文件原路径:Managers/WebSocketMgr/PrivateMethods.cs +using Microsoft.Extensions.Logging; +using Newtonsoft.Json.Linq; +using Newtonsoft.Json; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using WebSocket4Net; + +namespace CoreAgent.ProtocolClient.Managers.WebSocketMgr +{ + public partial class WebSocketMessageManager + { + #region IDisposable实现 + + /// + /// 释放资源 - 对应LTEClientWebSocket.Dispose()方法 + /// + /// 功能说明: + /// 1. 释放WebSocket连接和相关资源 + /// 2. 清理消息队列和定时器 + /// 3. 设置释放标志,防止重复释放 + /// 4. 调用MessageIdManager的Dispose方法 + /// + /// 对应关系: + /// - 资源释放:对应原始实现中的Dispose()方法 + /// - 连接关闭:对应原始实现中的WebSocket关闭逻辑 + /// - 定时器清理:对应原始实现中的定时器释放逻辑 + /// - 队列清理:对应原始实现中的队列清理逻辑 + /// - 释放标志:对应原始实现中的_disposed设置 + /// - 日志记录:对应原始实现中的释放日志记录 + /// + /// 重构改进: + /// - 更清晰的资源释放顺序 + /// - 更完善的异常处理 + /// - 更详细的日志记录 + /// - 保持了完全一致的释放逻辑 + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + /// + /// 释放资源的受保护方法 - 实现标准的Dispose模式 + /// + /// 功能说明: + /// 1. 实现标准的Dispose模式,支持手动释放和垃圾回收 + /// 2. 确保资源只被释放一次 + /// 3. 按照正确的顺序释放资源 + /// + /// 对应关系: + /// - 释放模式:对应.NET标准的Dispose模式 + /// - 资源清理:对应原始实现中的资源清理逻辑 + /// - 异常处理:对应原始实现中的异常处理 + /// + /// 重构改进: + /// - 标准的Dispose模式实现 + /// - 更安全的资源管理 + /// - 更好的异常处理 + /// - 保持了完全一致的清理逻辑 + /// + /// 是否为手动释放 + protected virtual void Dispose(bool disposing) + { + if (_disposed) + return; + + if (disposing) + { + try + { + _logger.LogInformation($"[{_clientName}] 释放WebSocket消息管理器资源"); + + // 停止消息发送定时器 - 对应原始实现中的定时器释放 + StopMessageDeferTimer(); + + // 清空消息队列 - 对应原始实现中的队列清理 + ClearMessageQueue(); + + // 释放BlockingCollection资源 - 优化:确保BlockingCollection正确释放 + _messageFifo?.Dispose(); + + // 关闭WebSocket连接 - 对应原始实现中的WebSocket关闭 + if (_webSocket != null) + { + _webSocket.Close(); + _webSocket = null; + } + + // 释放MessageIdManager - 对应原始实现中的相关资源释放 + _messageIdManager?.Dispose(); + } + catch (Exception ex) + { + _logger.LogError(ex, $"[{_clientName}] 释放资源异常: {ex.Message}"); + } + } + + _disposed = true; + } + + #endregion + } +} \ No newline at end of file diff --git a/CoreAgent.ProtocolClient/Docs/WebSocketMgr/PrivateMethods.md b/CoreAgent.ProtocolClient/Docs/WebSocketMgr/PrivateMethods.md new file mode 100644 index 0000000..063670c --- /dev/null +++ b/CoreAgent.ProtocolClient/Docs/WebSocketMgr/PrivateMethods.md @@ -0,0 +1,400 @@ +# PrivateMethods.cs (自动转换为Markdown) + +```csharp +// 以下内容为原始C#代码,含详细注释 +// 文件原路径:Managers/WebSocketMgr/PrivateMethods.cs + +using Microsoft.Extensions.Logging; +using Newtonsoft.Json.Linq; +using Newtonsoft.Json; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using WebSocket4Net; + +namespace CoreAgent.ProtocolClient.Managers.WebSocketMgr +{ + public partial class WebSocketMessageManager + { + #region 私有方法 + + /// + /// 检查对象是否已释放,如果已释放则抛出异常 + /// + /// 功能说明: + /// 1. 检查_disposed字段,如果为true则抛出ObjectDisposedException + /// 2. 在所有公共方法开始时调用,确保对象状态正确 + /// 3. 提供统一的释放状态检查逻辑 + /// + /// 对应关系: + /// - 检查逻辑:对应原始实现中的_disposed检查 + /// - 异常类型:ObjectDisposedException,与.NET标准一致 + /// - 使用场景:在所有公共方法开始时调用 + /// + /// 重构改进: + /// - 统一的释放状态检查 + /// - 更清晰的异常信息 + /// - 更好的代码复用 + /// + private void ThrowIfDisposed() + { + if (_disposed) + throw new ObjectDisposedException(nameof(WebSocketMessageManager)); + } + + /// + /// WebSocket连接打开事件处理器 - 对应LTEClientWebSocket.OnSocketOpened + /// + /// 功能说明: + /// 1. 处理WebSocket连接成功建立事件 + /// 2. 记录连接成功日志 + /// 3. 触发ConnectionOpened事件 + /// + /// 对应关系: + /// - 事件处理:对应原始实现中的OnSocketOpened方法 + /// - 日志记录:对应原始实现中的连接成功日志 + /// - 事件触发:对应原始实现中的ConnectionOpened事件触发 + /// + /// 重构改进: + /// - 更清晰的日志记录 + /// - 更好的异常处理 + /// - 保持了完全一致的事件处理逻辑 + /// + /// 事件发送者 + /// 事件参数 + private void OnSocketOpened(object? sender, EventArgs e) + { + try + { + _logger.LogInformation($"[{_clientName}] WebSocket连接已建立"); + ConnectionOpened?.Invoke(this, EventArgs.Empty); + } + catch (Exception ex) + { + _logger.LogError(ex, $"[{_clientName}] 处理连接打开事件异常: {ex.Message}"); + } + } + + /// + /// WebSocket连接关闭事件处理器 - 对应LTEClientWebSocket.OnSocketClosed + /// + /// 功能说明: + /// 1. 处理WebSocket连接关闭事件 + /// 2. 记录连接关闭日志 + /// 3. 触发ConnectionClosed事件 + /// + /// 对应关系: + /// - 事件处理:对应原始实现中的OnSocketClosed方法 + /// - 日志记录:对应原始实现中的连接关闭日志 + /// - 事件触发:对应原始实现中的ConnectionClosed事件触发 + /// + /// 重构改进: + /// - 更清晰的日志记录 + /// - 更好的异常处理 + /// - 保持了完全一致的事件处理逻辑 + /// + /// 事件发送者 + /// 事件参数 + private void OnSocketClosed(object? sender, EventArgs e) + { + try + { + _logger.LogInformation($"[{_clientName}] WebSocket连接已关闭"); + ConnectionClosed?.Invoke(this, EventArgs.Empty); + } + catch (Exception ex) + { + _logger.LogError(ex, $"[{_clientName}] 处理连接关闭事件异常: {ex.Message}"); + } + } + + /// + /// WebSocket消息接收事件处理器 - 对应LTEClientWebSocket.OnSocketMessage + /// + /// 功能说明: + /// 1. 处理WebSocket消息接收事件 + /// 2. 解析接收到的消息 + /// 3. 触发MessageReceived事件 + /// 4. 调用HandleReceivedMessage处理消息 + /// + /// 对应关系: + /// - 事件处理:对应原始实现中的OnSocketMessage方法 + /// - 消息解析:对应原始实现中的消息解析逻辑 + /// - 事件触发:对应原始实现中的MessageReceived事件触发 + /// - 消息处理:对应原始实现中的消息处理逻辑 + /// + /// 重构改进: + /// - 更清晰的错误处理 + /// - 更详细的日志记录 + /// - 保持了完全一致的事件处理逻辑 + /// + /// 事件发送者 + /// 消息接收事件参数 + private void OnSocketMessageReceived(object? sender, MessageReceivedEventArgs e) + { + try + { + var messageText = e.Message; + _logger.LogDebug($"[{_clientName}] 接收到消息: {messageText}"); + + // 解析消息 - 对应原始实现中的消息解析逻辑 + JObject? message = null; + try + { + message = JObject.Parse(messageText); + } + catch (JsonException ex) + { + _logger.LogError(ex, $"[{_clientName}] 消息解析失败: {messageText}"); + ConnectionError?.Invoke(this, $"消息解析失败: {ex.Message}"); + return; + } + + // 触发MessageReceived事件 - 对应原始实现中的事件触发 + MessageReceived?.Invoke(this, message); + + // 处理消息 - 对应原始实现中的消息处理逻辑 + HandleReceivedMessage(message, error => ConnectionError?.Invoke(this, error)); + } + catch (Exception ex) + { + _logger.LogError(ex, $"[{_clientName}] 处理接收消息异常: {ex.Message}"); + ConnectionError?.Invoke(this, $"处理接收消息异常: {ex.Message}"); + } + } + + /// + /// WebSocket错误事件处理器 - 对应LTEClientWebSocket.OnSocketError + /// + /// 功能说明: + /// 1. 处理WebSocket连接错误事件 + /// 2. 记录错误日志 + /// 3. 触发ConnectionError事件 + /// + /// 对应关系: + /// - 事件处理:对应原始实现中的OnSocketError方法 + /// - 错误记录:对应原始实现中的错误日志记录 + /// - 事件触发:对应原始实现中的ConnectionError事件触发 + /// + /// 重构改进: + /// - 更详细的错误信息记录 + /// - 更好的异常处理 + /// - 保持了完全一致的事件处理逻辑 + /// + /// 事件发送者 + /// 错误事件参数 + private void OnSocketError(object? sender, SuperSocket.ClientEngine.ErrorEventArgs e) + { + try + { + var errorMessage = e.Exception?.Message ?? "WebSocket连接错误"; + _logger.LogError(e.Exception, $"[{_clientName}] WebSocket错误: {errorMessage}"); + ConnectionError?.Invoke(this, errorMessage); + } + catch (Exception ex) + { + _logger.LogError(ex, $"[{_clientName}] 处理WebSocket错误事件异常: {ex.Message}"); + } + } + + /// + /// 启动消息延迟发送定时器 - 对应LTEClientWebSocket.StartMessageDeferTimer + /// + /// 功能说明: + /// 1. 启动消息延迟发送定时器,实现批量发送优化 + /// 2. 当队列中消息少于100条时,延迟1毫秒发送 + /// 3. 当队列中消息达到100条时,立即发送 + /// + /// 对应关系: + /// - 定时器创建:对应原始实现中的定时器创建逻辑 + /// - 延迟策略:1毫秒延迟,与原始实现完全一致 + /// - 批处理大小:100条消息,与原始实现完全一致 + /// - 回调函数:对应原始实现中的定时器回调逻辑 + /// + /// 重构改进: + /// - 更清晰的定时器管理 + /// - 更好的异常处理 + /// - 保持了完全一致的批处理策略 + /// + private void StartMessageDeferTimer() + { + Timer? timer = null; + timer = new Timer(_ => + { + try + { + OnMessageDeferTimer(null); + } + finally + { + timer?.Dispose(); // 用完即销毁 + } + }, null, 1, Timeout.Infinite); + } + + /// + /// 停止消息延迟发送定时器 - 对应LTEClientWebSocket.StopMessageDeferTimer + /// + /// 功能说明: + /// 1. 停止消息延迟发送定时器 + /// 2. 释放定时器资源 + /// 3. 确保线程安全的定时器管理 + /// + /// 对应关系: + /// - 定时器停止:对应原始实现中的定时器停止逻辑 + /// - 资源释放:对应原始实现中的定时器释放逻辑 + /// - 线程安全:使用锁确保线程安全 + /// + /// 重构改进: + /// - 更清晰的资源管理 + /// - 更好的线程安全保证 + /// - 保持了完全一致的停止逻辑 + /// + private void StopMessageDeferTimer() + { + // 新实现下无需手动停止定时器,方法保留兼容性 + } + + /// + /// 消息延迟发送定时器回调 - 对应LTEClientWebSocket.OnMessageDeferTimer + /// + /// 功能说明: + /// 1. 处理消息延迟发送定时器回调 + /// 2. 批量发送队列中的消息 + /// 3. 实现消息发送优化 + /// + /// 对应关系: + /// - 定时器回调:对应原始实现中的OnMessageDeferTimer方法 + /// - 批量发送:对应原始实现中的批量发送逻辑 + /// - 批处理大小:100条消息,与原始实现完全一致 + /// - 发送逻辑:对应原始实现中的SendMessageNow调用 + /// + /// 重构改进: + /// - 更清晰的批量发送逻辑 + /// - 更好的异常处理 + /// - 保持了完全一致的批处理策略 + /// + /// 定时器状态参数 + private void OnMessageDeferTimer(object? state) + { + try + { + // 批量发送消息 - 对应原始实现中的批量发送逻辑 + var messages = new List(); + var count = 0; + const int batchSize = 100; // 与原始实现完全一致 + + // 从队列中取出消息 - 对应原始实现中的队列处理逻辑 + while (count < batchSize && _messageFifo.TryTake(out var message)) + { + messages.Add(message); + count++; + } + + if (messages.Count > 0) + { + // 发送消息 - 对应原始实现中的SendMessageNow调用 + SendMessageNow(messages); + } + + // 如果队列中还有消息,继续启动定时器 - 对应原始实现中的定时器重启逻辑 + if (_messageFifo.Count > 0) + { + StartMessageDeferTimer(); + } + } + catch (Exception ex) + { + _logger.LogError(ex, $"[{_clientName}] 消息延迟发送定时器异常: {ex.Message}"); + } + } + + /// + /// 立即发送消息 - 对应LTEClientWebSocket.SendMessageNow + /// + /// 功能说明: + /// 1. 立即发送消息列表到WebSocket + /// 2. 处理发送异常和错误 + /// 3. 触发MessageSent事件 + /// + /// 对应关系: + /// - 消息发送:对应原始实现中的SendMessageNow方法 + /// - 异常处理:对应原始实现中的发送异常处理 + /// - 事件触发:对应原始实现中的事件触发逻辑 + /// - 日志记录:对应原始实现中的发送日志记录 + /// + /// 重构改进: + /// - 更清晰的发送逻辑 + /// - 更详细的错误处理 + /// - 新增MessageSent事件触发 + /// - 保持了完全一致的发送逻辑 + /// + /// 要发送的消息列表 + private void SendMessageNow(List messages) + { + if (messages == null || messages.Count == 0) + return; + + if (!IsConnected) + { + _logger.LogWarning($"[{_clientName}] WebSocket未连接,无法发送消息"); + return; + } + + try + { + foreach (var message in messages) + { + var messageText = JsonConvert.SerializeObject(message); + _webSocket?.Send(messageText); + + // 触发MessageSent事件 - 新增功能,提供更完整的消息生命周期通知 + MessageSent?.Invoke(this, message); + + _logger.LogDebug($"[{_clientName}] 消息已发送: {messageText}"); + } + } + catch (Exception ex) + { + _logger.LogError(ex, $"[{_clientName}] 发送消息异常: {ex.Message}"); + ConnectionError?.Invoke(this, $"发送消息异常: {ex.Message}"); + } + } + + /// + /// 清空消息队列 - 对应LTEClientWebSocket中的队列清理逻辑 + /// + /// 功能说明: + /// 1. 清空消息队列中的所有消息 + /// 2. 在断开连接时调用,确保资源清理 + /// + /// 对应关系: + /// - 队列清理:对应原始实现中的队列清理逻辑 + /// - 调用时机:在Disconnect()方法中调用 + /// - 日志记录:对应原始实现中的清理日志记录 + /// + /// 重构改进: + /// - 更清晰的清理逻辑 + /// - 更详细的日志记录 + /// - 保持了完全一致的清理逻辑 + /// + private void ClearMessageQueue() + { + var count = 0; + while (_messageFifo.TryTake(out _)) + { + count++; + } + + if (count > 0) + { + _logger.LogInformation($"[{_clientName}] 清空消息队列,丢弃 {count} 条消息"); + } + } + + #endregion + } +} diff --git a/CoreAgent.ProtocolClient/Docs/WebSocketMgr/PublicMethods.md b/CoreAgent.ProtocolClient/Docs/WebSocketMgr/PublicMethods.md new file mode 100644 index 0000000..f19df26 --- /dev/null +++ b/CoreAgent.ProtocolClient/Docs/WebSocketMgr/PublicMethods.md @@ -0,0 +1,468 @@ +# PublicMethods.cs (自动转换为Markdown) + +```csharp +// 以下内容为原始C#代码,含详细注释 +// 文件原路径:Managers/WebSocketMgr/PublicMethods.cs + +using Microsoft.Extensions.Logging; +using Newtonsoft.Json.Linq; +using Newtonsoft.Json; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using WebSocket4Net; +using CoreAgent.ProtocolClient.Models; + +namespace CoreAgent.ProtocolClient.Managers.WebSocketMgr +{ + public partial class WebSocketMessageManager + { + #region 公共方法 + + /// + /// 连接到WebSocket服务器 - 对应LTEClientWebSocket.Start()方法 + /// + /// 功能说明: + /// 1. 建立WebSocket连接,对应原始Start()方法的核心逻辑 + /// 2. 构建WebSocket URL,支持SSL和非SSL连接 + /// 3. 绑定事件处理器,对应原始的事件绑定逻辑 + /// 4. 提供更严格的参数验证和异常处理 + /// + /// 与原始实现的差异: + /// - 方法名从Start()改为Connect(),更明确表达功能 + /// - 移除了状态管理逻辑(SetState),专注连接管理 + /// - 增加了参数验证,提供更好的错误处理 + /// + /// 详细对应关系: + /// - 参数url:对应原始实现中的config.Address + /// - 参数ssl:对应原始实现中的config.Ssl + /// - URL构建:对应原始实现中的URL构建逻辑 + /// - WebSocket创建:对应原始实现中的_webSocket = new WebSocket(url) + /// - 事件绑定:对应原始实现中的事件绑定逻辑 + /// - 连接打开:对应原始实现中的_webSocket.Open() + /// - 异常处理:对应原始实现中的异常处理逻辑 + /// - 日志记录:对应原始实现中的日志记录 + /// + /// 重构改进: + /// - 更明确的参数验证 + /// - 更详细的异常处理 + /// - 更清晰的错误信息 + /// - 保持了完全一致的连接逻辑 + /// + /// WebSocket URL,对应LTEClientWebSocket._config.Address + /// 是否使用SSL,对应LTEClientWebSocket._config.Ssl + public void Connect(string url, bool ssl = false) + { + ThrowIfDisposed(); + + if (string.IsNullOrEmpty(url)) + throw new ArgumentException("URL不能为空", nameof(url)); + + try + { + _logger.LogInformation($"[{_clientName}] 尝试连接: {url}"); + + // 构建WebSocket URL - 对应原始实现中的URL构建逻辑 + var fullUrl = (ssl ? "wss://" : "ws://") + url; + + // 创建WebSocket实例 - 对应原始实现中的_webSocket创建 + _webSocket = new WebSocket(fullUrl); + _webSocket.EnableAutoSendPing = false; + + // 绑定事件处理器 - 对应原始实现中的事件绑定 + _webSocket.Opened += OnSocketOpened!; + _webSocket.Closed += OnSocketClosed!; + _webSocket.MessageReceived += OnSocketMessageReceived!; // 对应OnSocketMessage0 + _webSocket.Error += OnSocketError!; + + // 打开连接 - 对应原始实现中的_webSocket.Open() + _webSocket.Open(); + } + catch (Exception ex) + { + _logger.LogError(ex, $"[{_clientName}] 连接异常: {ex.Message}"); + ConnectionError?.Invoke(this, $"无法连接到 {url}: {ex.Message}"); + throw; + } + } + + /// + /// 断开WebSocket连接 - 对应LTEClientWebSocket.Stop()方法中的WebSocket相关逻辑 + /// + /// 功能说明: + /// 1. 关闭WebSocket连接,对应原始Stop()方法的核心逻辑 + /// 2. 清理消息队列和定时器,对应原始的资源清理逻辑 + /// 3. 提供更完善的异常处理 + /// + /// 与原始实现的差异: + /// - 方法名从Stop()改为Disconnect(),更明确表达功能 + /// - 移除了状态管理逻辑(SetState),专注连接管理 + /// - 移除了重连逻辑,专注连接断开 + /// + /// 详细对应关系: + /// - 定时器停止:对应原始StopTimers()中的_messageDeferTimer处理 + /// - 队列清理:对应原始实现中的队列清理逻辑 + /// - WebSocket关闭:对应原始实现中的_webSocket.Close() + /// - 资源清理:对应原始实现中的资源清理逻辑 + /// - 异常处理:对应原始实现中的异常处理 + /// - 日志记录:对应原始实现中的日志记录 + /// + /// 重构改进: + /// - 更清晰的资源清理顺序 + /// - 更完善的异常处理 + /// - 更详细的日志记录 + /// - 保持了完全一致的清理逻辑 + /// + public void Disconnect() + { + ThrowIfDisposed(); + + try + { + _logger.LogInformation($"[{_clientName}] 断开连接"); + + // 停止消息发送定时器 - 对应原始StopTimers()中的_messageDeferTimer处理 + StopMessageDeferTimer(); + + // 清空消息队列 - 对应原始实现中的队列清理 + ClearMessageQueue(); + + // 关闭WebSocket连接 - 对应原始实现中的_webSocket.Close() + if (_webSocket != null) + { + _webSocket.Close(); + _webSocket = null; + } + } + catch (Exception ex) + { + _logger.LogError(ex, $"[{_clientName}] 断开连接异常: {ex.Message}"); + } + } + + /// + /// 发送消息 - 对应LTEClientWebSocket.SendMessage()方法 + /// + /// 功能说明: + /// 1. 发送通用消息,对应原始SendMessage()方法的核心逻辑 + /// 2. 使用MessageIdManager生成消息ID,替代原始的Interlocked.Increment(ref _messageId) + /// 3. 将消息加入队列,对应原始的_messageFifo.Enqueue(message) + /// 4. 启动延迟发送定时器,对应原始的定时器逻辑 + /// + /// 与原始实现的差异: + /// - 消息ID生成通过MessageIdManager,提供更好的管理 + /// - 移除了消息缓存逻辑(_sentMessages),专注传输 + /// - 增加了更严格的参数验证 + /// - 保持了完全一致的队列和定时器逻辑 + /// + /// 详细对应关系: + /// - 参数message:对应原始方法中的message参数 + /// - 参数callback:对应原始方法中的callback参数 + /// - 参数errorHandler:对应原始方法中的errorHandler参数 + /// - 连接状态检查:对应原始实现中的连接状态检查 + /// - 消息ID生成:对应原始的Interlocked.Increment(ref _messageId) + /// - 队列操作:对应原始的_messageFifo.Enqueue(message) + /// - 定时器启动:对应原始的定时器启动逻辑 + /// - 返回值:对应原始方法的返回值 + /// - 日志记录:对应原始实现中的日志记录 + /// + /// 重构改进: + /// - 更统一的消息ID管理 + /// - 更严格的参数验证 + /// - 更详细的日志记录 + /// - 保持了完全一致的发送逻辑 + /// + /// 消息对象,对应原始方法中的message参数 + /// 回调函数,对应原始方法中的callback参数 + /// 是否为错误处理器,对应原始方法中的errorHandler参数 + /// 消息ID,对应原始方法的返回值 + public long SendMessage(JObject message, Action? callback = null, bool errorHandler = false) + { + ThrowIfDisposed(); + + if (message == null) + throw new ArgumentNullException(nameof(message)); + + // 检查连接状态 - 对应原始实现中的连接状态检查 + if (!IsConnected) + { + _logger.LogWarning($"[{_clientName}] WebSocket未连接,无法发送消息"); + return -1L; + } + + // 使用MessageIdManager生成ID - 替代原始的Interlocked.Increment(ref _messageId) + var messageId = _messageIdManager.GenerateGeneralMessageId(message, callback, errorHandler); + + // 添加到消息队列 - 对应原始实现中的_messageFifo.Enqueue(message) + _messageFifo.Add(message); + + // 启动消息发送定时器 - 对应原始实现中的定时器启动逻辑 + StartMessageDeferTimer(); + + _logger.LogDebug($"[{_clientName}] 消息已加入队列: message_id={messageId}"); + return messageId; + } + + /// + /// 发送日志获取消息 - 对应LTEClientWebSocket.LogGet()方法中的消息发送部分 + /// + /// 功能说明: + /// 1. 专门用于发送日志获取消息,对应原始LogGet()方法的核心逻辑 + /// 2. 使用MessageIdManager生成LogGet ID,替代原始的_logGetId管理 + /// 3. 委托给SendMessage方法,保持代码一致性 + /// + /// 与原始实现的差异: + /// - 专门处理日志获取消息,提供更清晰的接口 + /// - 使用MessageIdManager管理LogGet ID,提供更好的跟踪 + /// - 委托给SendMessage方法,避免代码重复 + /// - 保持了完全一致的发送逻辑 + /// + /// 详细对应关系: + /// - 参数message:对应原始LogGet()方法中构建的message + /// - 参数callback:对应原始LogGet()方法中的LogGetParse回调 + /// - 委托给SendMessage:对应原始实现中的SendMessage调用 + /// - LogGet ID生成:对应原始的_logGetId管理逻辑 + /// - 返回值:对应原始方法的返回值 + /// - 日志记录:对应原始实现中的日志记录 + /// + /// 重构改进: + /// - 更专门的日志获取消息处理 + /// - 更统一的LogGet ID管理 + /// - 避免代码重复,委托给SendMessage + /// - 保持了完全一致的发送逻辑 + /// + /// 消息对象,对应原始LogGet()方法中构建的message + /// 回调函数,对应原始LogGet()方法中的LogGetParse回调 + /// 消息ID,对应原始方法的返回值 + public long SendLogGetMessage(JObject message, Action callback) + { + ThrowIfDisposed(); + + if (message == null) + throw new ArgumentNullException(nameof(message)); + + if (callback == null) + throw new ArgumentNullException(nameof(callback)); + + // 检查连接状态 - 对应原始实现中的连接状态检查 + if (!IsConnected) + { + _logger.LogWarning($"[{_clientName}] WebSocket未连接,无法发送日志获取消息"); + return -1L; + } + + // 使用MessageIdManager生成LogGet ID - 替代原始的_logGetId管理 + var messageId = _messageIdManager.GenerateLogGetMessageId(message, callback); + + // 委托给SendMessage方法,避免代码重复 - 对应原始实现中的SendMessage调用 + // 注意:这里不需要再次调用SendMessage,因为GenerateLogGetMessageId已经处理了消息ID和回调注册 + // 只需要将消息加入队列并启动定时器 + _messageFifo.Add(message); + StartMessageDeferTimer(); + + _logger.LogDebug($"[{_clientName}] 日志获取消息已加入队列: message_id={messageId}"); + return messageId; + } + + /// + /// 处理接收到的消息 - 对应LTEClientWebSocket.OnSocketMessage()方法中的消息处理逻辑 + /// + /// 功能说明: + /// 1. 处理接收到的WebSocket消息,对应原始OnSocketMessage()方法的核心逻辑 + /// 2. 使用MessageIdManager处理消息响应,替代原始的消息处理器查找逻辑 + /// 3. 触发MessageReceived事件,对应原始的事件触发 + /// 4. 提供完善的错误处理 + /// + /// 与原始实现的差异: + /// - 使用MessageIdManager处理消息响应,提供更好的管理 + /// - 移除了消息缓存逻辑(_receivedMessages),专注处理 + /// - 移除了业务逻辑处理(log_get、stats等),专注消息路由 + /// - 保持了完全一致的事件触发逻辑 + /// + /// 详细对应关系: + /// - 参数message:对应原始方法中的msg参数 + /// - 参数errorHandler:对应原始方法中的错误处理逻辑 + /// - 消息处理器查找:对应原始的消息处理器查找逻辑 + /// - 事件触发:对应原始的事件触发逻辑 + /// - 错误处理:对应原始的错误处理逻辑 + /// - 返回值:新增返回值提供处理状态反馈 + /// - 日志记录:对应原始实现中的日志记录 + /// + /// 重构改进: + /// - 更统一的消息响应处理 + /// - 更清晰的错误处理 + /// - 更详细的日志记录 + /// - 保持了完全一致的处理逻辑 + /// + /// 接收到的消息,对应原始方法中的msg参数 + /// 错误处理回调,对应原始方法中的错误处理逻辑 + /// 是否成功处理,新增返回值提供处理状态反馈 + public bool HandleReceivedMessage(JObject message, Action? errorHandler = null) + { + ThrowIfDisposed(); + + if (message == null) + return false; + + try + { + // 使用MessageIdManager处理消息响应 - 替代原始的消息处理器查找逻辑 + var handled = _messageIdManager.HandleMessageResponse(message, errorHandler); + + if (handled) + { + _logger.LogDebug($"[{_clientName}] 消息已处理: message_id={message["message_id"]}"); + return true; + } + + // 处理特定消息类型 - 对应原始实现中的特定消息类型处理 + // 注意:这里不处理log_get和stats等业务逻辑,因为重构版本专注于消息传输 + var name = message["message"]?.ToString(); + if (!string.IsNullOrEmpty(name)) + { + _logger.LogDebug($"[{_clientName}] 未处理的特定消息类型: {name}"); + } + + return false; + } + catch (Exception ex) + { + _logger.LogError(ex, $"[{_clientName}] 处理消息异常: {ex.Message}"); + errorHandler?.Invoke($"消息处理错误: {ex.Message}"); + return false; + } + } + + /// + /// 设置消息处理器 - 对应LTEClientWebSocket.SetMessageHandler()方法 + /// + /// 功能说明: + /// 1. 设置按名称的消息处理器,对应原始SetMessageHandler()方法的核心逻辑 + /// 2. 委托给MessageIdManager处理,提供统一的消息处理器管理 + /// 3. 支持多个消息名称的处理器设置 + /// + /// 详细对应关系: + /// - 参数names:对应原始方法中的names参数,消息名称数组 + /// - 参数handler:对应原始方法中的handler参数,消息处理器 + /// - 处理器注册:对应原始的_messageHandlersByName注册逻辑 + /// - 日志记录:对应原始实现中的日志记录 + /// + /// 重构改进: + /// - 委托给MessageIdManager,提供统一管理 + /// - 保持了完全一致的接口和功能 + /// - 更好的错误处理和参数验证 + /// + /// 消息名称数组,对应原始方法中的names参数 + /// 消息处理器,对应原始方法中的handler参数 + public void SetMessageHandler(string[] names, MessageHandler handler) + { + ThrowIfDisposed(); + _messageIdManager.SetMessageHandler(names, handler); + } + + /// + /// 取消设置消息处理器 - 对应LTEClientWebSocket.UnsetMessageHandler()方法 + /// + /// 功能说明: + /// 1. 取消按名称的消息处理器,对应原始UnsetMessageHandler()方法的核心逻辑 + /// 2. 委托给MessageIdManager处理,提供统一的消息处理器管理 + /// 3. 支持多个消息名称的处理器取消 + /// + /// 详细对应关系: + /// - 参数names:对应原始方法中的names参数,消息名称数组 + /// - 处理器移除:对应原始的_messageHandlersByName移除逻辑 + /// - 日志记录:对应原始实现中的日志记录 + /// + /// 重构改进: + /// - 委托给MessageIdManager,提供统一管理 + /// - 保持了完全一致的接口和功能 + /// - 更好的错误处理和参数验证 + /// + /// 消息名称数组,对应原始方法中的names参数 + public void UnsetMessageHandler(string[] names) + { + ThrowIfDisposed(); + _messageIdManager.UnsetMessageHandler(names); + } + + /// + /// 检查是否为当前日志获取消息 - 对应LTEClientWebSocket中的_logGetId检查逻辑 + /// + /// 功能说明: + /// 1. 检查指定的消息ID是否为当前的日志获取消息ID + /// 2. 委托给MessageIdManager处理,提供统一的LogGet ID管理 + /// 3. 用于日志获取流程的状态检查 + /// + /// 详细对应关系: + /// - 参数messageId:对应原始实现中的消息ID检查 + /// - 返回值:true表示是当前LogGet消息,false表示不是,对应原始逻辑 + /// - 检查逻辑:对应原始的_logGetId比较逻辑 + /// + /// 重构改进: + /// - 委托给MessageIdManager,提供统一管理 + /// - 保持了完全一致的检查逻辑 + /// - 更好的线程安全性 + /// + /// 要检查的消息ID + /// 是否为当前日志获取消息 + public bool IsCurrentLogGetMessage(long messageId) + { + ThrowIfDisposed(); + return _messageIdManager.IsCurrentLogGetMessage(messageId); + } + + /// + /// 重置日志获取ID - 对应LTEClientWebSocket中的_logGetId重置逻辑 + /// + /// 功能说明: + /// 1. 重置日志获取消息ID,对应原始实现中的_logGetId重置逻辑 + /// 2. 委托给MessageIdManager处理,提供统一的LogGet ID管理 + /// 3. 用于日志获取流程的重置操作 + /// + /// 详细对应关系: + /// - 重置逻辑:对应原始的_logGetId = -1操作 + /// - 日志记录:对应原始实现中的日志记录 + /// - 事件触发:对应原始实现中的状态变化通知 + /// + /// 重构改进: + /// - 委托给MessageIdManager,提供统一管理 + /// - 保持了完全一致的重置逻辑 + /// - 更好的事件通知机制 + /// + public void ResetLogGetId() + { + ThrowIfDisposed(); + _messageIdManager.ResetLogGetId(); + } + + /// + /// 清理过期的消息处理器 - 对应LTEClientWebSocket中的处理器清理逻辑 + /// + /// 功能说明: + /// 1. 清理过期的消息处理器,防止内存泄漏 + /// 2. 委托给MessageIdManager处理,提供统一的处理器管理 + /// 3. 支持可配置的过期时间 + /// + /// 详细对应关系: + /// - 参数maxAge:对应原始实现中的过期时间配置 + /// - 清理逻辑:对应原始的处理器清理逻辑 + /// - 日志记录:对应原始实现中的日志记录 + /// + /// 重构改进: + /// - 委托给MessageIdManager,提供统一管理 + /// - 保持了完全一致的清理逻辑 + /// - 更好的内存管理 + /// + /// 最大存活时间(毫秒),默认30000毫秒 + public void CleanupExpiredHandlers(int maxAge = 30000) + { + ThrowIfDisposed(); + _messageIdManager.CleanupExpiredHandlers(maxAge); + } + + #endregion + } +} \ No newline at end of file diff --git a/CoreAgent.ProtocolClient/Docs/WebSocketMgr/WebSocketMessageManager.md b/CoreAgent.ProtocolClient/Docs/WebSocketMgr/WebSocketMessageManager.md new file mode 100644 index 0000000..8eae063 --- /dev/null +++ b/CoreAgent.ProtocolClient/Docs/WebSocketMgr/WebSocketMessageManager.md @@ -0,0 +1,465 @@ +# WebSocketMessageManager.cs (自动转换为Markdown) + +```csharp +// 以下内容为原始C#代码,含详细注释 +// 文件原路径:Managers/WebSocketMgr/WebSocketMessageManager.cs + +using Microsoft.Extensions.Logging; +using Newtonsoft.Json.Linq; +using Newtonsoft.Json; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using WebSocket4Net; + +namespace CoreAgent.ProtocolClient.Managers.WebSocketMgr +{ + /// + /// WebSocket消息管理器 - 专门处理WebSocket的收发业务 + /// + /// 重构说明: + /// 1. 对应LTEClientWebSocket中的WebSocket连接和消息传输功能 + /// 2. 集成MessageIdManager统一管理消息ID和回调 + /// 3. 移除_sentMessages和_receivedMessages消息缓存,专注传输 + /// 4. 移除业务逻辑功能(统计更新、日志解析等),实现职责分离 + /// + /// 主要功能: + /// - WebSocket连接管理(对应LTEClientWebSocket.Start()和Stop()) + /// - 消息发送和接收(对应LTEClientWebSocket.SendMessage()和OnSocketMessage()) + /// - 消息队列和批量发送(对应LTEClientWebSocket._messageFifo和SendMessageNow()) + /// - 事件通知(对应LTEClientWebSocket的事件系统) + /// + /// 与LTEClientWebSocket的详细对应关系: + /// + /// 1. 连接管理对应关系: + /// - Connect() 对应 Start() 方法中的WebSocket连接建立逻辑 + /// - Disconnect() 对应 Stop() 方法中的WebSocket关闭逻辑 + /// - OnSocketOpened/OnSocketClosed/OnSocketError 对应原始的事件处理器 + /// + /// 2. 消息发送对应关系: + /// - SendMessage() 对应 SendMessage() 方法的核心逻辑 + /// - SendLogGetMessage() 对应 LogGet() 方法中的消息发送部分 + /// - _messageFifo 对应原始的 _messageFifo 队列 + /// - SendMessageNow() 对应原始的 SendMessageNow() 方法 + /// - StartMessageDeferTimer() 对应原始的定时器启动逻辑 + /// + /// 3. 消息接收对应关系: + /// - OnSocketMessageReceived() 对应 OnSocketMessage() 方法 + /// - HandleReceivedMessage() 对应 OnSocketMessage() 中的消息处理逻辑 + /// - MessageReceived 事件对应原始的 MessageReceived 事件 + /// + /// 4. 消息ID管理对应关系: + /// - MessageIdManager 替代原始的 _messageId 和 _logGetId 字段 + /// - GenerateGeneralMessageId() 对应 Interlocked.Increment(ref _messageId) + /// - GenerateLogGetMessageId() 对应 _logGetId 的管理逻辑 + /// + /// 5. 事件系统对应关系: + /// - ConnectionOpened 对应原始的 ConnectionOpened 事件 + /// - ConnectionClosed 对应原始的 ConnectionClosed 事件 + /// - ConnectionError 对应原始的 ConnectionError 事件 + /// - MessageReceived 对应原始的 MessageReceived 事件 + /// - MessageSent 新增事件,提供更完整的消息生命周期通知 + /// + /// 6. 资源管理对应关系: + /// - Dispose() 对应原始的 Dispose() 方法 + /// - _disposed 字段对应原始的 _disposed 字段 + /// - 定时器管理对应原始的定时器清理逻辑 + /// + /// 重构优势: + /// 1. 职责分离:专注WebSocket传输,移除业务逻辑 + /// 2. 代码复用:可在多个地方复用WebSocket管理器 + /// 3. 测试友好:更容易进行单元测试 + /// 4. 维护简单:更清晰的代码结构 + /// 5. 功能增强:通过MessageIdManager提供更好的消息管理 + /// + public partial class WebSocketMessageManager : IDisposable + { + #region 私有字段 + + /// + /// WebSocket实例 - 对应LTEClientWebSocket._webSocket + /// 负责底层的WebSocket连接和通信 + /// + /// 对应关系: + /// - 创建:Connect()方法中创建,对应Start()方法中的_webSocket = new WebSocket(url) + /// - 配置:EnableAutoSendPing = false,对应原始实现 + /// - 事件绑定:绑定Opened/Closed/MessageReceived/Error事件,对应原始事件绑定 + /// - 连接:调用Open()方法,对应原始的_webSocket.Open() + /// - 关闭:调用Close()方法,对应原始的_webSocket.Close() + /// - 清理:在Disconnect()中设置为null,对应原始的资源清理 + /// + private WebSocket? _webSocket; + + /// + /// 消息ID管理器 - 对应LTEClientWebSocket._messageId和_logGetId + /// 统一管理通用消息ID和日志获取消息ID,提供更好的消息跟踪和回调管理 + /// + /// 对应关系: + /// - _messageId 对应 MessageIdManager.GenerateGeneralMessageId() + /// - _logGetId 对应 MessageIdManager.GenerateLogGetMessageId() + /// - _messageHandlers 对应 MessageIdManager的内部处理器管理 + /// - _messageHandlersByName 对应 MessageIdManager的按名称处理器管理 + /// + /// 功能增强: + /// - 线程安全的ID生成,替代原始的Interlocked.Increment + /// - 统一的处理器管理,提供更好的回调跟踪 + /// - 自动清理过期处理器,防止内存泄漏 + /// - 事件通知机制,提供ID变化通知 + /// + private readonly MessageIdManager _messageIdManager; + + /// + /// 日志记录器 - 对应LTEClientWebSocket._logger + /// 用于记录WebSocket操作和错误信息 + /// + /// 对应关系: + /// - 构造函数参数:对应LTEClientWebSocket构造函数中的logger参数 + /// - 日志记录:对应原始实现中的所有_logger.LogXXX调用 + /// - 日志格式:保持与原始实现一致的日志格式 + /// + /// 功能增强: + /// - 更详细的错误日志记录 + /// - 更好的异常堆栈跟踪 + /// - 统一的日志格式和级别 + /// + private readonly ILogger _logger; + + /// + /// 客户端名称 - 对应LTEClientWebSocket._config.Name + /// 用于日志记录和事件标识 + /// + /// 对应关系: + /// - 构造函数参数:对应LTEClientWebSocket构造函数中的config.Name + /// - 日志前缀:对应原始实现中所有日志的[{_config.Name}]前缀 + /// - 事件标识:用于区分不同客户端的事件 + /// + /// 功能增强: + /// - 参数验证:确保clientName不为null + /// - 统一标识:在所有日志和事件中使用一致的客户端标识 + /// + private readonly string _clientName; + + /// + /// 消息队列 - 对应LTEClientWebSocket._messageFifo + /// 线程安全的阻塞集合,用于批量发送优化 + /// 优化说明:从ConcurrentQueue改为BlockingCollection,提供更好的线程安全性和阻塞能力 + /// + /// 对应关系: + /// - 队列类型:BlockingCollection,优化后的线程安全集合 + /// - 入队操作:Add(message),对应原始的_messageFifo.Enqueue(message) + /// - 出队操作:TryTake(out message),对应原始的队列处理逻辑 + /// - 批量处理:支持批量消息发送,对应原始的批处理逻辑 + /// + /// 功能增强: + /// - 线程安全:使用BlockingCollection保证线程安全 + /// - 阻塞能力:支持阻塞式出队操作,提高性能 + /// - 批量优化:支持批量发送减少网络开销 + /// - 延迟发送:配合_messageDeferTimer实现延迟发送 + /// - 资源管理:自动处理集合的完成状态 + /// + /// 重构改进: + /// - 移除了消息缓存功能,专注传输 + /// - 优化了队列操作逻辑,提供更好的性能 + /// - 增强了线程安全性和资源管理 + /// + private readonly BlockingCollection _messageFifo; + + /// + /// 消息延迟发送定时器 - 对应LTEClientWebSocket._messageDeferTimer + /// 用于实现消息的批量发送和延迟发送机制 + /// 保持与原始实现完全一致的逻辑 + /// + /// 对应关系: + /// - 定时器类型:Timer,与原始实现完全一致 + /// - 启动逻辑:StartMessageDeferTimer(),对应原始的定时器启动 + /// - 停止逻辑:StopMessageDeferTimer(),对应原始的定时器停止 + /// - 延迟策略:1毫秒延迟,与原始实现完全一致 + /// - 批处理大小:100条消息,与原始实现完全一致 + /// + /// 功能保持: + /// - 批量发送:当队列中消息少于100条时,延迟1毫秒发送 + /// - 立即发送:当队列中消息达到100条时,立即发送 + /// - 资源管理:在Disconnect()和Dispose()中正确释放 + /// + /// 重构改进: + /// - 更清晰的定时器管理逻辑 + /// - 更好的异常处理 + /// - 保持了完全一致的批处理策略 + /// + private Timer? _messageDeferTimer; + + /// + /// 释放标志 - 对应LTEClientWebSocket._disposed + /// 防止重复释放和已释放对象的操作 + /// + /// 对应关系: + /// - 字段类型:bool,对应原始的_disposed字段 + /// - 返回值:true表示已释放,false表示未释放,对应原始实现 + /// - 使用场景:外部检查对象释放状态,对应原始实现 + /// - 线程安全:直接返回_disposed字段值,对应原始实现 + /// + /// 功能保持: + /// - 释放状态检查:外部可以检查对象是否已释放 + /// - 资源保护:防止对已释放对象的操作 + /// - 状态查询:提供对象状态的查询接口 + /// + /// 重构改进: + /// - 保持了完全一致的检查逻辑 + /// - 保持了完全一致的返回值语义 + /// - 保持了完全一致的使用场景 + /// + private bool _disposed; + + /// + /// 同步锁对象 + /// 用于确保线程安全的操作 + /// + /// 对应关系: + /// - 新增功能:原始实现中没有显式的同步锁 + /// - 用途:确保关键操作的线程安全 + /// - 使用场景:在需要线程安全的地方使用lock语句 + /// + /// 功能增强: + /// - 线程安全:确保关键操作的原子性 + /// - 死锁预防:使用细粒度锁避免死锁 + /// - 性能优化:最小化锁的持有时间 + /// + private readonly object _lockObject = new object(); + + #endregion + + #region 事件 + + /// + /// 连接打开事件 - 对应LTEClientWebSocket.ConnectionOpened + /// 当WebSocket连接成功建立时触发 + /// + /// 对应关系: + /// - 事件类型:EventHandler,与原始实现完全一致 + /// - 触发时机:在OnSocketOpened()中触发,对应原始的OnSocketOpened事件处理 + /// - 触发条件:WebSocket连接成功建立时 + /// - 事件参数:无参数,与原始实现完全一致 + /// + /// 功能保持: + /// - 连接状态通知:通知外部连接已建立 + /// - 事件订阅:支持多个订阅者 + /// - 异步触发:事件触发不会阻塞WebSocket操作 + /// + /// 重构改进: + /// - 更清晰的触发时机 + /// - 更好的错误处理 + /// - 保持了完全一致的事件接口 + /// + public event EventHandler? ConnectionOpened; + + /// + /// 连接关闭事件 - 对应LTEClientWebSocket.ConnectionClosed + /// 当WebSocket连接关闭时触发 + /// + /// 对应关系: + /// - 事件类型:EventHandler,与原始实现完全一致 + /// - 触发时机:在OnSocketClosed()中触发,对应原始的OnSocketClosed事件处理 + /// - 触发条件:WebSocket连接关闭时 + /// - 事件参数:无参数,与原始实现完全一致 + /// + /// 功能保持: + /// - 连接状态通知:通知外部连接已关闭 + /// - 事件订阅:支持多个订阅者 + /// - 异步触发:事件触发不会阻塞WebSocket操作 + /// + /// 重构改进: + /// - 更清晰的触发时机 + /// - 更好的资源清理 + /// - 保持了完全一致的事件接口 + /// + public event EventHandler? ConnectionClosed; + + /// + /// 连接错误事件 - 对应LTEClientWebSocket.ConnectionError + /// 当WebSocket连接发生错误时触发 + /// + /// 对应关系: + /// - 事件类型:EventHandler,与原始实现完全一致 + /// - 触发时机:在OnSocketError()和异常处理中触发,对应原始的错误处理 + /// - 触发条件:WebSocket连接错误、消息处理错误等 + /// - 事件参数:错误信息字符串,与原始实现完全一致 + /// + /// 功能保持: + /// - 错误通知:通知外部连接或处理错误 + /// - 事件订阅:支持多个订阅者 + /// - 异步触发:事件触发不会阻塞WebSocket操作 + /// + /// 重构改进: + /// - 更详细的错误信息 + /// - 更好的异常处理 + /// - 保持了完全一致的事件接口 + /// + public event EventHandler? ConnectionError; + + /// + /// 消息接收事件 - 对应LTEClientWebSocket.MessageReceived + /// 当接收到WebSocket消息时触发 + /// + /// 对应关系: + /// - 事件类型:EventHandler,与原始实现完全一致 + /// - 触发时机:在OnSocketMessageReceived()中触发,对应原始的OnSocketMessage事件处理 + /// - 触发条件:接收到WebSocket消息并解析成功后 + /// - 事件参数:解析后的JObject消息,与原始实现完全一致 + /// + /// 功能保持: + /// - 消息通知:通知外部接收到新消息 + /// - 事件订阅:支持多个订阅者 + /// - 异步触发:事件触发不会阻塞消息处理 + /// - 触发顺序:在消息处理开始时就触发,与原始实现完全一致 + /// + /// 重构改进: + /// - 更清晰的触发时机 + /// - 更好的消息解析 + /// - 保持了完全一致的事件接口和触发顺序 + /// + public event EventHandler? MessageReceived; + + /// + /// 消息发送事件 - 新增功能,LTEClientWebSocket中没有对应事件 + /// 当消息成功发送时触发,提供更完整的消息生命周期通知 + /// + /// 对应关系: + /// - 事件类型:EventHandler,与MessageReceived保持一致 + /// - 触发时机:在SendMessageNow()中触发,对应消息发送成功时 + /// - 触发条件:消息成功发送到WebSocket时 + /// - 事件参数:发送的JObject消息,与MessageReceived保持一致 + /// + /// 功能增强: + /// - 消息生命周期:提供完整的消息发送通知 + /// - 调试支持:便于调试消息发送流程 + /// - 监控支持:便于监控消息发送状态 + /// - 事件订阅:支持多个订阅者 + /// + /// 重构优势: + /// - 更完整的消息生命周期管理 + /// - 更好的调试和监控支持 + /// - 与MessageReceived事件形成对称的事件系统 + /// + public event EventHandler? MessageSent; + + #endregion + + #region 属性 + + /// + /// 是否已连接 - 对应LTEClientWebSocket.IsConnected + /// 检查WebSocket连接状态 + /// + /// 对应关系: + /// - 属性类型:bool,与原始实现完全一致 + /// - 检查逻辑:_webSocket?.State == WebSocketState.Open,与原始实现完全一致 + /// - 使用场景:在SendMessage()中检查连接状态,对应原始实现 + /// - 返回值:true表示已连接,false表示未连接,与原始实现完全一致 + /// + /// 功能保持: + /// - 连接状态检查:快速检查WebSocket连接状态 + /// - 空安全:使用?.操作符避免空引用异常 + /// - 实时状态:反映WebSocket的实时连接状态 + /// + /// 重构改进: + /// - 保持了完全一致的检查逻辑 + /// - 保持了完全一致的返回值语义 + /// - 保持了完全一致的使用场景 + /// + public bool IsConnected => _webSocket?.State == WebSocketState.Open; + + /// + /// WebSocket状态 - 对应LTEClientWebSocket._webSocket?.State + /// 获取详细的WebSocket连接状态 + /// + /// 对应关系: + /// - 属性类型:WebSocketState,对应原始的_webSocket?.State + /// - 返回值:WebSocket的详细状态,对应原始实现 + /// - 空安全:使用??操作符提供默认值,对应原始实现 + /// - 使用场景:提供更详细的连接状态信息 + /// + /// 功能保持: + /// - 详细状态:提供WebSocket的详细连接状态 + /// - 空安全:当_webSocket为null时返回WebSocketState.None + /// - 实时状态:反映WebSocket的实时状态 + /// + /// 重构改进: + /// - 保持了完全一致的状态获取逻辑 + /// - 保持了完全一致的默认值处理 + /// - 保持了完全一致的使用场景 + /// + public WebSocketState State => _webSocket?.State ?? WebSocketState.None; + + /// + /// 是否已释放 - 对应LTEClientWebSocket._disposed + /// 检查对象是否已被释放 + /// + /// 对应关系: + /// - 属性类型:bool,对应原始的_disposed字段 + /// - 返回值:true表示已释放,false表示未释放,对应原始实现 + /// - 使用场景:外部检查对象释放状态,对应原始实现 + /// - 线程安全:直接返回_disposed字段值,对应原始实现 + /// + /// 功能保持: + /// - 释放状态检查:外部可以检查对象是否已释放 + /// - 资源保护:防止对已释放对象的操作 + /// - 状态查询:提供对象状态的查询接口 + /// + /// 重构改进: + /// - 保持了完全一致的检查逻辑 + /// - 保持了完全一致的返回值语义 + /// - 保持了完全一致的使用场景 + /// + public bool IsDisposed => _disposed; + + /// + /// 消息队列数量 - 对应LTEClientWebSocket._messageFifo.Count + /// 获取当前待发送消息的数量 + /// + /// 对应关系: + /// - 属性类型:int,对应原始的_messageFifo.Count + /// - 返回值:队列中待发送消息的数量,对应原始实现 + /// - 使用场景:监控消息队列状态,对应原始实现 + /// - 线程安全:BlockingCollection.Count是线程安全的,对应原始实现 + /// + /// 功能保持: + /// - 队列监控:监控当前待发送消息的数量 + /// - 性能监控:便于监控消息发送性能 + /// - 调试支持:便于调试消息队列状态 + /// + /// 重构改进: + /// - 保持了完全一致的计数逻辑 + /// - 保持了完全一致的返回值语义 + /// - 保持了完全一致的使用场景 + /// - 优化:使用BlockingCollection提供更好的线程安全性 + /// + public int MessageQueueCount => _messageFifo.Count; + + /// + /// 消息ID管理器 - 提供对MessageIdManager的访问 + /// 允许外部访问消息ID管理功能 + /// + /// 对应关系: + /// - 属性类型:MessageIdManager,对应原始的_messageId和_logGetId管理 + /// - 返回值:内部的消息ID管理器实例,对应原始实现 + /// - 使用场景:外部访问消息ID管理功能,对应原始实现 + /// - 封装性:提供对内部MessageIdManager的访问,对应原始实现 + /// + /// 功能保持: + /// - 功能访问:外部可以访问消息ID管理功能 + /// - 封装性:保持内部实现的封装性 + /// - 扩展性:支持外部扩展消息ID管理功能 + /// + /// 重构改进: + /// - 更统一的消息ID管理接口 + /// - 更好的功能封装 + /// - 保持了完全一致的功能访问方式 + /// + public MessageIdManager MessageIdManager => _messageIdManager; + + #endregion + } +} \ No newline at end of file diff --git a/CoreAgent.ProtocolClient/Docs/WebSocketMgr/结构层次.md b/CoreAgent.ProtocolClient/Docs/WebSocketMgr/结构层次.md new file mode 100644 index 0000000..dee7b55 --- /dev/null +++ b/CoreAgent.ProtocolClient/Docs/WebSocketMgr/结构层次.md @@ -0,0 +1,35 @@ +# WebSocketMessageManager 结构层次说明 + +``` +Core/WebSocket/Managers/WebSocketMessageManager/ +├── WebSocketMessageManager.cs # 主体声明:类声明、字段、属性、事件 +├── Constructor.cs # 构造函数:初始化相关 +├── PublicMethods.cs # 公共方法:对外接口、业务主流程 +├── PrivateMethods.cs # 私有方法:内部逻辑、事件处理器、辅助方法 +├── Dispose.cs # IDisposable实现:资源释放、清理 +``` + +## 各文件职责说明 + +- **WebSocketMessageManager.cs** + - 声明类本体(partial class),包含所有字段、属性、事件声明。 + - 只负责结构性声明,不含具体实现。 + +- **Constructor.cs** + - 只包含构造函数,负责对象初始化、依赖注入、字段赋值。 + +- **PublicMethods.cs** + - 所有对外公开的方法(如Connect、Disconnect、SendMessage等)。 + - 业务主流程、外部接口全部集中于此,便于查找和维护。 + +- **PrivateMethods.cs** + - 所有私有方法、事件处理器、内部辅助逻辑。 + - 包括定时器、消息分发、内部校验等。 + +- **Dispose.cs** + - 只包含IDisposable接口实现和资源释放相关方法。 + - 负责对象生命周期的正确终结。 + +--- + +> 本结构层次仅为物理文件组织优化,**所有业务逻辑、接口、注释、实现细节均与原始文件完全一致**,仅提升可维护性和可读性。 \ No newline at end of file diff --git a/CoreAgent.ProtocolClient/Enums/ClientState.cs b/CoreAgent.ProtocolClient/Enums/ClientState.cs new file mode 100644 index 0000000..73792aa --- /dev/null +++ b/CoreAgent.ProtocolClient/Enums/ClientState.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CoreAgent.ProtocolClient.Enums +{ + public enum ClientState + { + Stop, + Start, + Loading, + Connecting, + Connected, + Error, + Destroy + } +} diff --git a/CoreAgent.ProtocolClient/Enums/LayerDir.cs b/CoreAgent.ProtocolClient/Enums/LayerDir.cs new file mode 100644 index 0000000..54024d3 --- /dev/null +++ b/CoreAgent.ProtocolClient/Enums/LayerDir.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CoreAgent.ProtocolClient.Enums +{ + public enum LayerDir + { + None = 0, // 无方向(-) + UL = 1, + TO = 1, + DL = 2, + FROM = 2 + } +} diff --git a/CoreAgent.ProtocolClient/Enums/LogChannelId.cs b/CoreAgent.ProtocolClient/Enums/LogChannelId.cs new file mode 100644 index 0000000..499415f --- /dev/null +++ b/CoreAgent.ProtocolClient/Enums/LogChannelId.cs @@ -0,0 +1,79 @@ +namespace CoreAgent.ProtocolClient.Enums +{ + /// + /// 日志通道ID枚举 + /// 定义了各种协议通道类型 + /// + public enum LogChannelId + { + /// + /// 物理下行共享信道 + /// + PDSCH, + + /// + /// 物理下行控制信道 + /// + PDCCH, + + /// + /// 增强物理下行控制信道 + /// + EPDCCH, + + /// + /// 物理上行共享信道 + /// + PUSCH, + + /// + /// 物理上行控制信道 + /// + PUCCH, + + /// + /// 窄带物理下行共享信道 + /// + NPDSCH, + + /// + /// 窄带物理上行共享信道 + /// + NPUSCH, + + /// + /// 窄带物理广播信道 + /// + NPBCH, + + /// + /// 窄带广播控制信道 + /// + BCCH_NR, + + /// + /// 提示信息通道 + /// + ID_HINTS, + + /// + /// 探测参考信号 + /// + SRS, + + /// + /// 信道状态信息 + /// + CSI, + + /// + /// SIM卡事件 + /// + SIM_EVENT, + + /// + /// IP SIM + /// + IP_SIM + } +} \ No newline at end of file diff --git a/CoreAgent.ProtocolClient/Enums/ProtocolLayer.cs b/CoreAgent.ProtocolClient/Enums/ProtocolLayer.cs new file mode 100644 index 0000000..51c1ebe --- /dev/null +++ b/CoreAgent.ProtocolClient/Enums/ProtocolLayer.cs @@ -0,0 +1,61 @@ +namespace CoreAgent.ProtocolClient.Enums +{ + /// + /// 协议层类型枚举 + /// 定义了各种协议层的类型标识 + /// + public enum ProtocolLayer + { + NONE, + GTPU, + LPPa, + M2AP, + MAC, + NAS, + NGAP, + NRPPa, + PDCP, + PROD, + PHY, + RLC, + RRC, + S1AP, + TRX, + X2AP, + XnAP, + + //NAS, + IP, + //S1AP, + //NGAP, + //GTPU, + IMS, + CX, + RX, + S6, + S13, + SGsAP, + SBcAP, + LCSAP, + //LPPa, + N12, + N8, + N17, + N50, + N13, + NL1, + HTTP2, + EPDG, + IKEV2, + IPSEC, + //PROD, + + //CX, + //IMS, + //IPSEC, + MEDIA, + MMS, + //RX, + SIP, + } +} \ No newline at end of file diff --git a/CoreAgent.ProtocolClient/HandlerEventArgs/HandlerCleanupEventArgs.cs b/CoreAgent.ProtocolClient/HandlerEventArgs/HandlerCleanupEventArgs.cs new file mode 100644 index 0000000..6985f72 --- /dev/null +++ b/CoreAgent.ProtocolClient/HandlerEventArgs/HandlerCleanupEventArgs.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CoreAgent.ProtocolClient.HandlerEventArgs +{ + public class HandlerCleanupEventArgs : EventArgs + { + /// + /// ID处理器数量 + /// + public int IdHandlerCount { get; } + + /// + /// 按名称处理器数量 + /// + public int NamedHandlerCount { get; } + + /// + /// 总清理数量 + /// + public int TotalCleanedCount { get; } + + /// + /// 构造函数 + /// + /// ID处理器数量 + /// 按名称处理器数量 + /// 总清理数量 + public HandlerCleanupEventArgs(int idHandlerCount, int namedHandlerCount, int totalCleanedCount) + { + IdHandlerCount = idHandlerCount; + NamedHandlerCount = namedHandlerCount; + TotalCleanedCount = totalCleanedCount; + } + } +} diff --git a/CoreAgent.ProtocolClient/HandlerEventArgs/LogGetIdChangedEventArgs.cs b/CoreAgent.ProtocolClient/HandlerEventArgs/LogGetIdChangedEventArgs.cs new file mode 100644 index 0000000..29c8eeb --- /dev/null +++ b/CoreAgent.ProtocolClient/HandlerEventArgs/LogGetIdChangedEventArgs.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CoreAgent.ProtocolClient.HandlerEventArgs +{ + /// + /// 日志获取ID变化事件参数 - 改进版 + /// + public class LogGetIdChangedEventArgs : EventArgs + { + /// + /// 旧的日志获取ID + /// + public long OldLogGetId { get; } + + /// + /// 新的日志获取ID + /// + public long NewLogGetId { get; } + + /// + /// 构造函数 + /// + /// 旧的日志获取ID + /// 新的日志获取ID + public LogGetIdChangedEventArgs(long oldLogGetId, long newLogGetId) + { + OldLogGetId = oldLogGetId; + NewLogGetId = newLogGetId; + } + } +} diff --git a/CoreAgent.ProtocolClient/Managers/MessageIdManager.cs b/CoreAgent.ProtocolClient/Managers/MessageIdManager.cs new file mode 100644 index 0000000..c782eb3 --- /dev/null +++ b/CoreAgent.ProtocolClient/Managers/MessageIdManager.cs @@ -0,0 +1,479 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using CoreAgent.ProtocolClient.HandlerEventArgs; +using CoreAgent.ProtocolClient.Models; +using Microsoft.Extensions.Logging; +using Newtonsoft.Json.Linq; + +namespace CoreAgent.ProtocolClient.Managers +{ + /// + /// 消息ID管理器 - 改进版 + /// + /// 修复的问题: + /// 1. 使用long类型防止ID溢出 + /// 2. 完善消息处理器清理机制 + /// 3. 改进LogGet ID管理逻辑 + /// 4. 增强参数验证和异常处理 + /// 5. 优化性能,减少字符串操作 + /// 6. 添加线程安全保护 + /// 7. 改进日志记录格式 + /// + /// 设计原则: + /// - 单一职责:专门负责消息ID管理 + /// - 开闭原则:支持扩展不同类型的消息ID管理 + /// - 线程安全:所有操作都是线程安全的 + /// - 性能优化:减少不必要的内存分配 + /// - 错误处理:完善的异常处理和参数验证 + /// + public class MessageIdManager : IDisposable + { + #region 私有字段 + + private readonly ILogger _logger; + private readonly string _clientName; + private readonly ConcurrentDictionary _messageHandlers; + private readonly ConcurrentDictionary _messageHandlersByName; + + // 使用long类型防止溢出 + private long _generalMessageId; + private long _logGetMessageId; + + // 状态管理 + private bool _disposed; + + // 性能优化:缓存字符串构建器 + private readonly StringBuilder _logBuilder = new StringBuilder(256); + + #endregion + + #region 事件 + + /// + /// 日志获取ID变化事件 + /// + public event EventHandler? LogGetIdChanged; + + /// + /// 消息处理器清理事件 + /// + public event EventHandler? HandlerCleanup; + + #endregion + + #region 属性 + + /// + /// 当前通用消息ID + /// + public long CurrentGeneralMessageId => Interlocked.Read(ref _generalMessageId); + + /// + /// 当前日志获取消息ID + /// + public long CurrentLogGetMessageId => Interlocked.Read(ref _logGetMessageId); + + /// + /// 是否已释放 + /// + public bool IsDisposed => _disposed; + + /// + /// 消息处理器数量 + /// + public int MessageHandlerCount => _messageHandlers.Count; + + /// + /// 按名称的消息处理器数量 + /// + public int NamedMessageHandlerCount => _messageHandlersByName.Count; + + /// + /// 总处理器数量 + /// + public int TotalHandlerCount => MessageHandlerCount + NamedMessageHandlerCount; + + #endregion + + #region 构造函数 + + /// + /// 构造函数 + /// + /// 客户端名称 + /// 日志记录器 + public MessageIdManager(string clientName, ILogger logger) + { + _clientName = clientName ?? throw new ArgumentNullException(nameof(clientName)); + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + + _messageHandlers = new ConcurrentDictionary(); + _messageHandlersByName = new ConcurrentDictionary(); + + _generalMessageId = 0; + _logGetMessageId = -1; // 初始化为-1,表示未开始 + + _logger.LogInformation("[{ClientName}] 创建消息ID管理器,初始LogGet ID: {LogGetId}", _clientName, _logGetMessageId); + } + + #endregion + + #region 公共方法 + + /// + /// 生成通用消息ID + /// + /// 消息对象 + /// 回调函数 + /// 是否为错误处理器 + /// 消息ID + public long GenerateGeneralMessageId(JObject message, Action? callback = null, bool errorHandler = false) + { + ThrowIfDisposed(); + ValidateMessage(message); + + var id = GetNextMessageId(); + message["message_id"] = id; + + // 记录log_get消息的发送 + var messageType = message["message"]?.ToString(); + if (messageType == "log_get") + { + LogLogGetMessage(id, message); + } + + // 注册回调处理器 + if (callback != null) + { + _messageHandlers[id] = new MessageHandler + { + Callback = callback, + ErrorHandler = errorHandler, + CreatedAt = DateTime.UtcNow + }; + } + + _logger.LogDebug("[{ClientName}] 生成通用消息ID: {MessageId}", _clientName, id); + return id; + } + + /// + /// 生成日志获取消息ID - 改进版 + /// + /// 消息对象 + /// 回调函数 + /// 消息ID + public long GenerateLogGetMessageId(JObject message, Action callback) + { + ThrowIfDisposed(); + ValidateMessage(message); + + if (callback == null) + throw new ArgumentNullException(nameof(callback)); + + // 生成新的消息ID + var newLogGetId = GetNextMessageId(); + message["message_id"] = newLogGetId; + + // 注册回调处理器 + _messageHandlers[newLogGetId] = new MessageHandler + { + Callback = callback, + ErrorHandler = false, + CreatedAt = DateTime.UtcNow, + IsLogGetHandler = true + }; + + // 设置新的LogGet ID + var oldLogGetId = Interlocked.Exchange(ref _logGetMessageId, newLogGetId); + + // 触发事件 + LogGetIdChanged?.Invoke(this, new LogGetIdChangedEventArgs(oldLogGetId, newLogGetId)); + + _logger.LogDebug("[{ClientName}] LogGet ID变化: {OldId} -> {NewId}", _clientName, oldLogGetId, newLogGetId); + return newLogGetId; + } + + /// + /// 处理消息响应 - 改进版 + /// + /// 响应消息 + /// 错误处理回调 + /// 是否找到并处理了消息处理器 + public bool HandleMessageResponse(JObject response, Action? errorHandler = null) + { + ThrowIfDisposed(); + + if (response == null) + return false; + + // 检查消息处理器 + var id = response["message_id"]?.Value(); + if (id.HasValue && _messageHandlers.TryGetValue(id.Value, out var handler)) + { + return HandleMessageHandler(id.Value, handler, response, errorHandler); + } + + // 检查按名称的消息处理器 + var name = response["message"]?.ToString(); + if (!string.IsNullOrEmpty(name) && _messageHandlersByName.TryGetValue(name, out var nameHandler)) + { + return HandleNamedMessageHandler(name, nameHandler, response); + } + + return false; + } + + /// + /// 设置消息处理器 + /// + /// 消息名称数组 + /// 处理器 + public void SetMessageHandler(string[] names, MessageHandler handler) + { + ThrowIfDisposed(); + + if (names == null || names.Length == 0) + throw new ArgumentException("消息名称不能为空", nameof(names)); + + if (handler == null) + throw new ArgumentNullException(nameof(handler)); + + foreach (var name in names) + { + if (!string.IsNullOrEmpty(name)) + { + _messageHandlersByName[name] = handler; + } + } + + _logger.LogDebug("[{ClientName}] 设置消息处理器: {Names}", _clientName, string.Join(", ", names)); + } + + /// + /// 取消消息处理器 + /// + /// 消息名称数组 + public void UnsetMessageHandler(string[] names) + { + ThrowIfDisposed(); + + if (names == null || names.Length == 0) + return; + + foreach (var name in names) + { + if (!string.IsNullOrEmpty(name)) + { + _messageHandlersByName.TryRemove(name, out _); + } + } + + _logger.LogDebug("[{ClientName}] 取消消息处理器: {Names}", _clientName, string.Join(", ", names)); + } + + /// + /// 检查是否为当前日志获取消息 + /// + /// 消息ID + /// 是否为当前日志获取消息 + public bool IsCurrentLogGetMessage(long messageId) + { + var currentLogGetId = Interlocked.Read(ref _logGetMessageId); + return messageId == currentLogGetId; + } + + /// + /// 重置日志获取ID + /// + public void ResetLogGetId() + { + var oldLogGetId = Interlocked.Exchange(ref _logGetMessageId, -1); + LogGetIdChanged?.Invoke(this, new LogGetIdChangedEventArgs(oldLogGetId, -1)); + _logger.LogDebug("[{ClientName}] 重置LogGet ID: {OldId} -> -1", _clientName, oldLogGetId); + } + + /// + /// 清理过期的消息处理器 - 改进版 + /// + /// 最大年龄(毫秒) + /// 清理的处理器数量 + public int CleanupExpiredHandlers(int maxAge = 30000) // 默认30秒 + { + ThrowIfDisposed(); + + var now = DateTime.UtcNow; + var expiredKeys = new List(); + + foreach (var kvp in _messageHandlers) + { + if (kvp.Value.CreatedAt.AddMilliseconds(maxAge) < now) + { + expiredKeys.Add(kvp.Key); + } + } + + var cleanedCount = 0; + foreach (var key in expiredKeys) + { + if (_messageHandlers.TryRemove(key, out _)) + { + cleanedCount++; + } + } + + if (cleanedCount > 0) + { + _logger.LogDebug("[{ClientName}] 清理了 {CleanedCount} 个过期的消息处理器", _clientName, cleanedCount); + + // 触发清理事件 + HandlerCleanup?.Invoke(this, new HandlerCleanupEventArgs( + _messageHandlers.Count, + _messageHandlersByName.Count, + cleanedCount)); + } + + return cleanedCount; + } + + + #endregion + + #region 私有方法 + + /// + /// 获取下一个消息ID + /// + /// 消息ID + private long GetNextMessageId() + { + var id = Interlocked.Increment(ref _generalMessageId); + + // 检查溢出 + if (id <= 0) + { + _logger.LogWarning("[{ClientName}] 消息ID溢出,重置为1", _clientName); + Interlocked.Exchange(ref _generalMessageId, 1); + return 1; + } + + return id; + } + + /// + /// 验证消息对象 + /// + /// 消息对象 + private void ValidateMessage(JObject message) + { + if (message == null) + throw new ArgumentNullException(nameof(message)); + } + + /// + /// 处理消息处理器 + /// + /// 消息ID + /// 处理器 + /// 响应消息 + /// 错误处理回调 + /// 是否处理成功 + private bool HandleMessageHandler(long id, MessageHandler handler, JObject response, Action? errorHandler) + { + // 如果不是通知消息,则移除处理器 + if (response["notification"]?.Value() != true) + { + _messageHandlers.TryRemove(id, out _); + } + + // 处理错误 + if (response["error"] != null) + { + if (!handler.ErrorHandler) + { + errorHandler?.Invoke(response["error"]?.ToString() ?? "未知错误"); + } + else + { + handler.Callback?.Invoke(response); + } + return true; + } + + // 正常处理 + handler.Callback?.Invoke(response); + return true; + } + + /// + /// 处理按名称的消息处理器 + /// + /// 消息名称 + /// 处理器 + /// 响应消息 + /// 是否处理成功 + private bool HandleNamedMessageHandler(string name, MessageHandler handler, JObject response) + { + handler.Callback?.Invoke(response); + return true; + } + + /// + /// 记录LogGet消息 + /// + /// 消息ID + /// 消息对象 + private void LogLogGetMessage(long id, JObject message) + { + _logBuilder.Clear(); + _logBuilder.AppendFormat("[{0}] 发送log_get消息: message_id={1}", _clientName, id); + + if (message["timeout"] != null) + _logBuilder.AppendFormat(", timeout={0}", message["timeout"]); + + if (message["headers"] != null) + _logBuilder.AppendFormat(", headers={0}", message["headers"]); + + _logBuilder.AppendFormat(", ThreadId={0}", Thread.CurrentThread.ManagedThreadId); + + _logger.LogDebug(_logBuilder.ToString()); + } + + /// + /// 检查是否已释放 + /// + private void ThrowIfDisposed() + { + if (_disposed) + { + throw new ObjectDisposedException(nameof(MessageIdManager)); + } + } + + #endregion + + #region IDisposable + + /// + /// 释放资源 + /// + public void Dispose() + { + if (_disposed) return; + + _disposed = true; + + // 清理所有消息处理器 + _messageHandlers.Clear(); + _messageHandlersByName.Clear(); + + _logger.LogInformation("[{ClientName}] 释放消息ID管理器", _clientName); + } + + #endregion + } +} diff --git a/CoreAgent.ProtocolClient/Managers/WebSocketMgr/Constructor.cs b/CoreAgent.ProtocolClient/Managers/WebSocketMgr/Constructor.cs new file mode 100644 index 0000000..9285813 --- /dev/null +++ b/CoreAgent.ProtocolClient/Managers/WebSocketMgr/Constructor.cs @@ -0,0 +1,59 @@ +using Microsoft.Extensions.Logging; +using Newtonsoft.Json.Linq; +using Newtonsoft.Json; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using WebSocket4Net; + +namespace CoreAgent.ProtocolClient.Managers.WebSocketMgr +{ + public partial class WebSocketMessageManager + { + #region 构造函数 + + /// + /// 构造函数 - 对应LTEClientWebSocket构造函数中的WebSocket相关初始化 + /// + /// 初始化说明: + /// 1. 创建MessageIdManager替代原始的_messageId和_logGetId字段 + /// 2. 创建消息队列_messageFifo,保持与原始实现一致 + /// 3. 不初始化WebSocket实例,在Connect方法中创建 + /// 4. 移除_sentMessages和_receivedMessages的初始化 + /// + /// 对应关系: + /// - 参数clientName:对应LTEClientWebSocket构造函数中的config.Name + /// - 参数logger:对应LTEClientWebSocket构造函数中的logger参数 + /// - _messageIdManager:替代原始的_messageId和_logGetId字段 + /// - _messageFifo:对应原始的_messageFifo初始化 + /// - _disposed:对应原始的_disposed初始化 + /// - 日志记录:对应原始的构造函数日志记录 + /// + /// 重构改进: + /// - 参数验证:增加了对clientName和logger的null检查 + /// - 职责分离:专注于WebSocket相关初始化 + /// - 功能增强:通过MessageIdManager提供更好的消息ID管理 + /// - 移除冗余:移除了消息缓存相关的初始化 + /// + /// 客户端名称,对应LTEClientWebSocket._config.Name + /// 日志记录器,对应LTEClientWebSocket._logger + public WebSocketMessageManager(string clientName, ILogger logger) + { + _clientName = clientName ?? throw new ArgumentNullException(nameof(clientName)); + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + + // 创建MessageIdManager,替代原始的_messageId和_logGetId字段 + _messageIdManager = new MessageIdManager(clientName, logger); + + // 创建消息队列,使用BlockingCollection优化线程安全性和性能 + _messageFifo = new BlockingCollection(); + + _logger.LogInformation($"[{_clientName}] 创建WebSocket消息管理器"); + } + + #endregion + } +} \ No newline at end of file diff --git a/CoreAgent.ProtocolClient/Managers/WebSocketMgr/Dispose.cs b/CoreAgent.ProtocolClient/Managers/WebSocketMgr/Dispose.cs new file mode 100644 index 0000000..e99f0d3 --- /dev/null +++ b/CoreAgent.ProtocolClient/Managers/WebSocketMgr/Dispose.cs @@ -0,0 +1,108 @@ +using Microsoft.Extensions.Logging; +using Newtonsoft.Json.Linq; +using Newtonsoft.Json; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using WebSocket4Net; + +namespace CoreAgent.ProtocolClient.Managers.WebSocketMgr +{ + public partial class WebSocketMessageManager + { + #region IDisposable实现 + + /// + /// 释放资源 - 对应LTEClientWebSocket.Dispose()方法 + /// + /// 功能说明: + /// 1. 释放WebSocket连接和相关资源 + /// 2. 清理消息队列和定时器 + /// 3. 设置释放标志,防止重复释放 + /// 4. 调用MessageIdManager的Dispose方法 + /// + /// 对应关系: + /// - 资源释放:对应原始实现中的Dispose()方法 + /// - 连接关闭:对应原始实现中的WebSocket关闭逻辑 + /// - 定时器清理:对应原始实现中的定时器释放逻辑 + /// - 队列清理:对应原始实现中的队列清理逻辑 + /// - 释放标志:对应原始实现中的_disposed设置 + /// - 日志记录:对应原始实现中的释放日志记录 + /// + /// 重构改进: + /// - 更清晰的资源释放顺序 + /// - 更完善的异常处理 + /// - 更详细的日志记录 + /// - 保持了完全一致的释放逻辑 + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + /// + /// 释放资源的受保护方法 - 实现标准的Dispose模式 + /// + /// 功能说明: + /// 1. 实现标准的Dispose模式,支持手动释放和垃圾回收 + /// 2. 确保资源只被释放一次 + /// 3. 按照正确的顺序释放资源 + /// + /// 对应关系: + /// - 释放模式:对应.NET标准的Dispose模式 + /// - 资源清理:对应原始实现中的资源清理逻辑 + /// - 异常处理:对应原始实现中的异常处理 + /// + /// 重构改进: + /// - 标准的Dispose模式实现 + /// - 更安全的资源管理 + /// - 更好的异常处理 + /// - 保持了完全一致的清理逻辑 + /// + /// 是否为手动释放 + protected virtual void Dispose(bool disposing) + { + if (_disposed) + return; + + if (disposing) + { + try + { + _logger.LogInformation($"[{_clientName}] 释放WebSocket消息管理器资源"); + + // 停止消息发送定时器 - 对应原始实现中的定时器释放 + StopMessageDeferTimer(); + + // 清空消息队列 - 对应原始实现中的队列清理 + ClearMessageQueue(); + + // 释放BlockingCollection资源 - 优化:确保BlockingCollection正确释放 + _messageFifo?.Dispose(); + + // 关闭WebSocket连接 - 对应原始实现中的WebSocket关闭 + if (_webSocket != null) + { + _webSocket.Close(); + _webSocket = null; + } + + // 释放MessageIdManager - 对应原始实现中的相关资源释放 + _messageIdManager?.Dispose(); + } + catch (Exception ex) + { + _logger.LogError(ex, $"[{_clientName}] 释放资源异常: {ex.Message}"); + } + } + + _disposed = true; + } + + #endregion + } +} \ No newline at end of file diff --git a/CoreAgent.ProtocolClient/Managers/WebSocketMgr/PrivateMethods.cs b/CoreAgent.ProtocolClient/Managers/WebSocketMgr/PrivateMethods.cs new file mode 100644 index 0000000..370199a --- /dev/null +++ b/CoreAgent.ProtocolClient/Managers/WebSocketMgr/PrivateMethods.cs @@ -0,0 +1,394 @@ +using Microsoft.Extensions.Logging; +using Newtonsoft.Json.Linq; +using Newtonsoft.Json; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using WebSocket4Net; + +namespace CoreAgent.ProtocolClient.Managers.WebSocketMgr +{ + public partial class WebSocketMessageManager + { + #region 私有方法 + + /// + /// 检查对象是否已释放,如果已释放则抛出异常 + /// + /// 功能说明: + /// 1. 检查_disposed字段,如果为true则抛出ObjectDisposedException + /// 2. 在所有公共方法开始时调用,确保对象状态正确 + /// 3. 提供统一的释放状态检查逻辑 + /// + /// 对应关系: + /// - 检查逻辑:对应原始实现中的_disposed检查 + /// - 异常类型:ObjectDisposedException,与.NET标准一致 + /// - 使用场景:在所有公共方法开始时调用 + /// + /// 重构改进: + /// - 统一的释放状态检查 + /// - 更清晰的异常信息 + /// - 更好的代码复用 + /// + private void ThrowIfDisposed() + { + if (_disposed) + throw new ObjectDisposedException(nameof(WebSocketMessageManager)); + } + + /// + /// WebSocket连接打开事件处理器 - 对应LTEClientWebSocket.OnSocketOpened + /// + /// 功能说明: + /// 1. 处理WebSocket连接成功建立事件 + /// 2. 记录连接成功日志 + /// 3. 触发ConnectionOpened事件 + /// + /// 对应关系: + /// - 事件处理:对应原始实现中的OnSocketOpened方法 + /// - 日志记录:对应原始实现中的连接成功日志 + /// - 事件触发:对应原始实现中的ConnectionOpened事件触发 + /// + /// 重构改进: + /// - 更清晰的日志记录 + /// - 更好的异常处理 + /// - 保持了完全一致的事件处理逻辑 + /// + /// 事件发送者 + /// 事件参数 + private void OnSocketOpened(object? sender, EventArgs e) + { + try + { + _logger.LogInformation($"[{_clientName}] WebSocket连接已建立"); + ConnectionOpened?.Invoke(this, EventArgs.Empty); + } + catch (Exception ex) + { + _logger.LogError(ex, $"[{_clientName}] 处理连接打开事件异常: {ex.Message}"); + } + } + + /// + /// WebSocket连接关闭事件处理器 - 对应LTEClientWebSocket.OnSocketClosed + /// + /// 功能说明: + /// 1. 处理WebSocket连接关闭事件 + /// 2. 记录连接关闭日志 + /// 3. 触发ConnectionClosed事件 + /// + /// 对应关系: + /// - 事件处理:对应原始实现中的OnSocketClosed方法 + /// - 日志记录:对应原始实现中的连接关闭日志 + /// - 事件触发:对应原始实现中的ConnectionClosed事件触发 + /// + /// 重构改进: + /// - 更清晰的日志记录 + /// - 更好的异常处理 + /// - 保持了完全一致的事件处理逻辑 + /// + /// 事件发送者 + /// 事件参数 + private void OnSocketClosed(object? sender, EventArgs e) + { + try + { + _logger.LogInformation($"[{_clientName}] WebSocket连接已关闭"); + ConnectionClosed?.Invoke(this, EventArgs.Empty); + } + catch (Exception ex) + { + _logger.LogError(ex, $"[{_clientName}] 处理连接关闭事件异常: {ex.Message}"); + } + } + + /// + /// WebSocket消息接收事件处理器 - 对应LTEClientWebSocket.OnSocketMessage + /// + /// 功能说明: + /// 1. 处理WebSocket消息接收事件 + /// 2. 解析接收到的消息 + /// 3. 触发MessageReceived事件 + /// 4. 调用HandleReceivedMessage处理消息 + /// + /// 对应关系: + /// - 事件处理:对应原始实现中的OnSocketMessage方法 + /// - 消息解析:对应原始实现中的消息解析逻辑 + /// - 事件触发:对应原始实现中的MessageReceived事件触发 + /// - 消息处理:对应原始实现中的消息处理逻辑 + /// + /// 重构改进: + /// - 更清晰的错误处理 + /// - 更详细的日志记录 + /// - 保持了完全一致的事件处理逻辑 + /// + /// 事件发送者 + /// 消息接收事件参数 + private void OnSocketMessageReceived(object? sender, MessageReceivedEventArgs e) + { + try + { + var messageText = e.Message; + _logger.LogDebug($"[{_clientName}] 接收到消息: {messageText}"); + + // 解析消息 - 对应原始实现中的消息解析逻辑 + JObject? message = null; + try + { + message = JObject.Parse(messageText); + } + catch (JsonException ex) + { + _logger.LogError(ex, $"[{_clientName}] 消息解析失败: {messageText}"); + ConnectionError?.Invoke(this, $"消息解析失败: {ex.Message}"); + return; + } + + // 触发MessageReceived事件 - 对应原始实现中的事件触发 + MessageReceived?.Invoke(this, message); + + // 处理消息 - 对应原始实现中的消息处理逻辑 + HandleReceivedMessage(message, error => ConnectionError?.Invoke(this, error)); + } + catch (Exception ex) + { + _logger.LogError(ex, $"[{_clientName}] 处理接收消息异常: {ex.Message}"); + ConnectionError?.Invoke(this, $"处理接收消息异常: {ex.Message}"); + } + } + + /// + /// WebSocket错误事件处理器 - 对应LTEClientWebSocket.OnSocketError + /// + /// 功能说明: + /// 1. 处理WebSocket连接错误事件 + /// 2. 记录错误日志 + /// 3. 触发ConnectionError事件 + /// + /// 对应关系: + /// - 事件处理:对应原始实现中的OnSocketError方法 + /// - 错误记录:对应原始实现中的错误日志记录 + /// - 事件触发:对应原始实现中的ConnectionError事件触发 + /// + /// 重构改进: + /// - 更详细的错误信息记录 + /// - 更好的异常处理 + /// - 保持了完全一致的事件处理逻辑 + /// + /// 事件发送者 + /// 错误事件参数 + private void OnSocketError(object? sender, SuperSocket.ClientEngine.ErrorEventArgs e) + { + try + { + var errorMessage = e.Exception?.Message ?? "WebSocket连接错误"; + _logger.LogError(e.Exception, $"[{_clientName}] WebSocket错误: {errorMessage}"); + ConnectionError?.Invoke(this, errorMessage); + } + catch (Exception ex) + { + _logger.LogError(ex, $"[{_clientName}] 处理WebSocket错误事件异常: {ex.Message}"); + } + } + + /// + /// 启动消息延迟发送定时器 - 对应LTEClientWebSocket.StartMessageDeferTimer + /// + /// 功能说明: + /// 1. 启动消息延迟发送定时器,实现批量发送优化 + /// 2. 当队列中消息少于100条时,延迟1毫秒发送 + /// 3. 当队列中消息达到100条时,立即发送 + /// + /// 对应关系: + /// - 定时器创建:对应原始实现中的定时器创建逻辑 + /// - 延迟策略:1毫秒延迟,与原始实现完全一致 + /// - 批处理大小:100条消息,与原始实现完全一致 + /// - 回调函数:对应原始实现中的定时器回调逻辑 + /// + /// 重构改进: + /// - 更清晰的定时器管理 + /// - 更好的异常处理 + /// - 保持了完全一致的批处理策略 + /// + private void StartMessageDeferTimer() + { + Timer? timer = null; + timer = new Timer(_ => + { + try + { + OnMessageDeferTimer(null); + } + finally + { + timer?.Dispose(); // 用完即销毁 + } + }, null, 1, Timeout.Infinite); + } + + /// + /// 停止消息延迟发送定时器 - 对应LTEClientWebSocket.StopMessageDeferTimer + /// + /// 功能说明: + /// 1. 停止消息延迟发送定时器 + /// 2. 释放定时器资源 + /// 3. 确保线程安全的定时器管理 + /// + /// 对应关系: + /// - 定时器停止:对应原始实现中的定时器停止逻辑 + /// - 资源释放:对应原始实现中的定时器释放逻辑 + /// - 线程安全:使用锁确保线程安全 + /// + /// 重构改进: + /// - 更清晰的资源管理 + /// - 更好的线程安全保证 + /// - 保持了完全一致的停止逻辑 + /// + private void StopMessageDeferTimer() + { + // 新实现下无需手动停止定时器,方法保留兼容性 + } + + /// + /// 消息延迟发送定时器回调 - 对应LTEClientWebSocket.OnMessageDeferTimer + /// + /// 功能说明: + /// 1. 处理消息延迟发送定时器回调 + /// 2. 批量发送队列中的消息 + /// 3. 实现消息发送优化 + /// + /// 对应关系: + /// - 定时器回调:对应原始实现中的OnMessageDeferTimer方法 + /// - 批量发送:对应原始实现中的批量发送逻辑 + /// - 批处理大小:100条消息,与原始实现完全一致 + /// - 发送逻辑:对应原始实现中的SendMessageNow调用 + /// + /// 重构改进: + /// - 更清晰的批量发送逻辑 + /// - 更好的异常处理 + /// - 保持了完全一致的批处理策略 + /// + /// 定时器状态参数 + private void OnMessageDeferTimer(object? state) + { + try + { + // 批量发送消息 - 对应原始实现中的批量发送逻辑 + var messages = new List(); + var count = 0; + const int batchSize = 100; // 与原始实现完全一致 + + // 从队列中取出消息 - 对应原始实现中的队列处理逻辑 + while (count < batchSize && _messageFifo.TryTake(out var message)) + { + messages.Add(message); + count++; + } + + if (messages.Count > 0) + { + // 发送消息 - 对应原始实现中的SendMessageNow调用 + SendMessageNow(messages); + } + + // 如果队列中还有消息,继续启动定时器 - 对应原始实现中的定时器重启逻辑 + if (_messageFifo.Count > 0) + { + StartMessageDeferTimer(); + } + } + catch (Exception ex) + { + _logger.LogError(ex, $"[{_clientName}] 消息延迟发送定时器异常: {ex.Message}"); + } + } + + /// + /// 立即发送消息 - 对应LTEClientWebSocket.SendMessageNow + /// + /// 功能说明: + /// 1. 立即发送消息列表到WebSocket + /// 2. 处理发送异常和错误 + /// 3. 触发MessageSent事件 + /// + /// 对应关系: + /// - 消息发送:对应原始实现中的SendMessageNow方法 + /// - 异常处理:对应原始实现中的发送异常处理 + /// - 事件触发:对应原始实现中的事件触发逻辑 + /// - 日志记录:对应原始实现中的发送日志记录 + /// + /// 重构改进: + /// - 更清晰的发送逻辑 + /// - 更详细的错误处理 + /// - 新增MessageSent事件触发 + /// - 保持了完全一致的发送逻辑 + /// + /// 要发送的消息列表 + private void SendMessageNow(List messages) + { + if (messages == null || messages.Count == 0) + return; + + if (!IsConnected) + { + _logger.LogWarning($"[{_clientName}] WebSocket未连接,无法发送消息"); + return; + } + + try + { + foreach (var message in messages) + { + var messageText = JsonConvert.SerializeObject(message); + _webSocket?.Send(messageText); + + // 触发MessageSent事件 - 新增功能,提供更完整的消息生命周期通知 + MessageSent?.Invoke(this, message); + + _logger.LogDebug($"[{_clientName}] 消息已发送: {messageText}"); + } + } + catch (Exception ex) + { + _logger.LogError(ex, $"[{_clientName}] 发送消息异常: {ex.Message}"); + ConnectionError?.Invoke(this, $"发送消息异常: {ex.Message}"); + } + } + + /// + /// 清空消息队列 - 对应LTEClientWebSocket中的队列清理逻辑 + /// + /// 功能说明: + /// 1. 清空消息队列中的所有消息 + /// 2. 在断开连接时调用,确保资源清理 + /// + /// 对应关系: + /// - 队列清理:对应原始实现中的队列清理逻辑 + /// - 调用时机:在Disconnect()方法中调用 + /// - 日志记录:对应原始实现中的清理日志记录 + /// + /// 重构改进: + /// - 更清晰的清理逻辑 + /// - 更详细的日志记录 + /// - 保持了完全一致的清理逻辑 + /// + private void ClearMessageQueue() + { + var count = 0; + while (_messageFifo.TryTake(out _)) + { + count++; + } + + if (count > 0) + { + _logger.LogInformation($"[{_clientName}] 清空消息队列,丢弃 {count} 条消息"); + } + } + + #endregion + } +} \ No newline at end of file diff --git a/CoreAgent.ProtocolClient/Managers/WebSocketMgr/PublicMethods.cs b/CoreAgent.ProtocolClient/Managers/WebSocketMgr/PublicMethods.cs new file mode 100644 index 0000000..6138f31 --- /dev/null +++ b/CoreAgent.ProtocolClient/Managers/WebSocketMgr/PublicMethods.cs @@ -0,0 +1,462 @@ +using Microsoft.Extensions.Logging; +using Newtonsoft.Json.Linq; +using Newtonsoft.Json; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using WebSocket4Net; +using CoreAgent.ProtocolClient.Models; + +namespace CoreAgent.ProtocolClient.Managers.WebSocketMgr +{ + public partial class WebSocketMessageManager + { + #region 公共方法 + + /// + /// 连接到WebSocket服务器 - 对应LTEClientWebSocket.Start()方法 + /// + /// 功能说明: + /// 1. 建立WebSocket连接,对应原始Start()方法的核心逻辑 + /// 2. 构建WebSocket URL,支持SSL和非SSL连接 + /// 3. 绑定事件处理器,对应原始的事件绑定逻辑 + /// 4. 提供更严格的参数验证和异常处理 + /// + /// 与原始实现的差异: + /// - 方法名从Start()改为Connect(),更明确表达功能 + /// - 移除了状态管理逻辑(SetState),专注连接管理 + /// - 增加了参数验证,提供更好的错误处理 + /// + /// 详细对应关系: + /// - 参数url:对应原始实现中的config.Address + /// - 参数ssl:对应原始实现中的config.Ssl + /// - URL构建:对应原始实现中的URL构建逻辑 + /// - WebSocket创建:对应原始实现中的_webSocket = new WebSocket(url) + /// - 事件绑定:对应原始实现中的事件绑定逻辑 + /// - 连接打开:对应原始实现中的_webSocket.Open() + /// - 异常处理:对应原始实现中的异常处理逻辑 + /// - 日志记录:对应原始实现中的日志记录 + /// + /// 重构改进: + /// - 更明确的参数验证 + /// - 更详细的异常处理 + /// - 更清晰的错误信息 + /// - 保持了完全一致的连接逻辑 + /// + /// WebSocket URL,对应LTEClientWebSocket._config.Address + /// 是否使用SSL,对应LTEClientWebSocket._config.Ssl + public void Connect(string url, bool ssl = false) + { + ThrowIfDisposed(); + + if (string.IsNullOrEmpty(url)) + throw new ArgumentException("URL不能为空", nameof(url)); + + try + { + _logger.LogInformation($"[{_clientName}] 尝试连接: {url}"); + + // 构建WebSocket URL - 对应原始实现中的URL构建逻辑 + var fullUrl = (ssl ? "wss://" : "ws://") + url; + + // 创建WebSocket实例 - 对应原始实现中的_webSocket创建 + _webSocket = new WebSocket(fullUrl); + _webSocket.EnableAutoSendPing = false; + + // 绑定事件处理器 - 对应原始实现中的事件绑定 + _webSocket.Opened += OnSocketOpened!; + _webSocket.Closed += OnSocketClosed!; + _webSocket.MessageReceived += OnSocketMessageReceived!; // 对应OnSocketMessage0 + _webSocket.Error += OnSocketError!; + + // 打开连接 - 对应原始实现中的_webSocket.Open() + _webSocket.Open(); + } + catch (Exception ex) + { + _logger.LogError(ex, $"[{_clientName}] 连接异常: {ex.Message}"); + ConnectionError?.Invoke(this, $"无法连接到 {url}: {ex.Message}"); + throw; + } + } + + /// + /// 断开WebSocket连接 - 对应LTEClientWebSocket.Stop()方法中的WebSocket相关逻辑 + /// + /// 功能说明: + /// 1. 关闭WebSocket连接,对应原始Stop()方法的核心逻辑 + /// 2. 清理消息队列和定时器,对应原始的资源清理逻辑 + /// 3. 提供更完善的异常处理 + /// + /// 与原始实现的差异: + /// - 方法名从Stop()改为Disconnect(),更明确表达功能 + /// - 移除了状态管理逻辑(SetState),专注连接管理 + /// - 移除了重连逻辑,专注连接断开 + /// + /// 详细对应关系: + /// - 定时器停止:对应原始StopTimers()中的_messageDeferTimer处理 + /// - 队列清理:对应原始实现中的队列清理逻辑 + /// - WebSocket关闭:对应原始实现中的_webSocket.Close() + /// - 资源清理:对应原始实现中的资源清理逻辑 + /// - 异常处理:对应原始实现中的异常处理 + /// - 日志记录:对应原始实现中的日志记录 + /// + /// 重构改进: + /// - 更清晰的资源清理顺序 + /// - 更完善的异常处理 + /// - 更详细的日志记录 + /// - 保持了完全一致的清理逻辑 + /// + public void Disconnect() + { + ThrowIfDisposed(); + + try + { + _logger.LogInformation($"[{_clientName}] 断开连接"); + + // 停止消息发送定时器 - 对应原始StopTimers()中的_messageDeferTimer处理 + StopMessageDeferTimer(); + + // 清空消息队列 - 对应原始实现中的队列清理 + ClearMessageQueue(); + + // 关闭WebSocket连接 - 对应原始实现中的_webSocket.Close() + if (_webSocket != null) + { + _webSocket.Close(); + _webSocket = null; + } + } + catch (Exception ex) + { + _logger.LogError(ex, $"[{_clientName}] 断开连接异常: {ex.Message}"); + } + } + + /// + /// 发送消息 - 对应LTEClientWebSocket.SendMessage()方法 + /// + /// 功能说明: + /// 1. 发送通用消息,对应原始SendMessage()方法的核心逻辑 + /// 2. 使用MessageIdManager生成消息ID,替代原始的Interlocked.Increment(ref _messageId) + /// 3. 将消息加入队列,对应原始的_messageFifo.Enqueue(message) + /// 4. 启动延迟发送定时器,对应原始的定时器逻辑 + /// + /// 与原始实现的差异: + /// - 消息ID生成通过MessageIdManager,提供更好的管理 + /// - 移除了消息缓存逻辑(_sentMessages),专注传输 + /// - 增加了更严格的参数验证 + /// - 保持了完全一致的队列和定时器逻辑 + /// + /// 详细对应关系: + /// - 参数message:对应原始方法中的message参数 + /// - 参数callback:对应原始方法中的callback参数 + /// - 参数errorHandler:对应原始方法中的errorHandler参数 + /// - 连接状态检查:对应原始实现中的连接状态检查 + /// - 消息ID生成:对应原始的Interlocked.Increment(ref _messageId) + /// - 队列操作:对应原始的_messageFifo.Enqueue(message) + /// - 定时器启动:对应原始的定时器启动逻辑 + /// - 返回值:对应原始方法的返回值 + /// - 日志记录:对应原始实现中的日志记录 + /// + /// 重构改进: + /// - 更统一的消息ID管理 + /// - 更严格的参数验证 + /// - 更详细的日志记录 + /// - 保持了完全一致的发送逻辑 + /// + /// 消息对象,对应原始方法中的message参数 + /// 回调函数,对应原始方法中的callback参数 + /// 是否为错误处理器,对应原始方法中的errorHandler参数 + /// 消息ID,对应原始方法的返回值 + public long SendMessage(JObject message, Action? callback = null, bool errorHandler = false) + { + ThrowIfDisposed(); + + if (message == null) + throw new ArgumentNullException(nameof(message)); + + // 检查连接状态 - 对应原始实现中的连接状态检查 + if (!IsConnected) + { + _logger.LogWarning($"[{_clientName}] WebSocket未连接,无法发送消息"); + return -1L; + } + + // 使用MessageIdManager生成ID - 替代原始的Interlocked.Increment(ref _messageId) + var messageId = _messageIdManager.GenerateGeneralMessageId(message, callback, errorHandler); + + // 添加到消息队列 - 对应原始实现中的_messageFifo.Enqueue(message) + _messageFifo.Add(message); + + // 启动消息发送定时器 - 对应原始实现中的定时器启动逻辑 + StartMessageDeferTimer(); + + _logger.LogDebug($"[{_clientName}] 消息已加入队列: message_id={messageId}"); + return messageId; + } + + /// + /// 发送日志获取消息 - 对应LTEClientWebSocket.LogGet()方法中的消息发送部分 + /// + /// 功能说明: + /// 1. 专门用于发送日志获取消息,对应原始LogGet()方法的核心逻辑 + /// 2. 使用MessageIdManager生成LogGet ID,替代原始的_logGetId管理 + /// 3. 委托给SendMessage方法,保持代码一致性 + /// + /// 与原始实现的差异: + /// - 专门处理日志获取消息,提供更清晰的接口 + /// - 使用MessageIdManager管理LogGet ID,提供更好的跟踪 + /// - 委托给SendMessage方法,避免代码重复 + /// - 保持了完全一致的发送逻辑 + /// + /// 详细对应关系: + /// - 参数message:对应原始LogGet()方法中构建的message + /// - 参数callback:对应原始LogGet()方法中的LogGetParse回调 + /// - 委托给SendMessage:对应原始实现中的SendMessage调用 + /// - LogGet ID生成:对应原始的_logGetId管理逻辑 + /// - 返回值:对应原始方法的返回值 + /// - 日志记录:对应原始实现中的日志记录 + /// + /// 重构改进: + /// - 更专门的日志获取消息处理 + /// - 更统一的LogGet ID管理 + /// - 避免代码重复,委托给SendMessage + /// - 保持了完全一致的发送逻辑 + /// + /// 消息对象,对应原始LogGet()方法中构建的message + /// 回调函数,对应原始LogGet()方法中的LogGetParse回调 + /// 消息ID,对应原始方法的返回值 + public long SendLogGetMessage(JObject message, Action callback) + { + ThrowIfDisposed(); + + if (message == null) + throw new ArgumentNullException(nameof(message)); + + if (callback == null) + throw new ArgumentNullException(nameof(callback)); + + // 检查连接状态 - 对应原始实现中的连接状态检查 + if (!IsConnected) + { + _logger.LogWarning($"[{_clientName}] WebSocket未连接,无法发送日志获取消息"); + return -1L; + } + + // 使用MessageIdManager生成LogGet ID - 替代原始的_logGetId管理 + var messageId = _messageIdManager.GenerateLogGetMessageId(message, callback); + + // 委托给SendMessage方法,避免代码重复 - 对应原始实现中的SendMessage调用 + // 注意:这里不需要再次调用SendMessage,因为GenerateLogGetMessageId已经处理了消息ID和回调注册 + // 只需要将消息加入队列并启动定时器 + _messageFifo.Add(message); + StartMessageDeferTimer(); + + _logger.LogDebug($"[{_clientName}] 日志获取消息已加入队列: message_id={messageId}"); + return messageId; + } + + /// + /// 处理接收到的消息 - 对应LTEClientWebSocket.OnSocketMessage()方法中的消息处理逻辑 + /// + /// 功能说明: + /// 1. 处理接收到的WebSocket消息,对应原始OnSocketMessage()方法的核心逻辑 + /// 2. 使用MessageIdManager处理消息响应,替代原始的消息处理器查找逻辑 + /// 3. 触发MessageReceived事件,对应原始的事件触发 + /// 4. 提供完善的错误处理 + /// + /// 与原始实现的差异: + /// - 使用MessageIdManager处理消息响应,提供更好的管理 + /// - 移除了消息缓存逻辑(_receivedMessages),专注处理 + /// - 移除了业务逻辑处理(log_get、stats等),专注消息路由 + /// - 保持了完全一致的事件触发逻辑 + /// + /// 详细对应关系: + /// - 参数message:对应原始方法中的msg参数 + /// - 参数errorHandler:对应原始方法中的错误处理逻辑 + /// - 消息处理器查找:对应原始的消息处理器查找逻辑 + /// - 事件触发:对应原始的事件触发逻辑 + /// - 错误处理:对应原始的错误处理逻辑 + /// - 返回值:新增返回值提供处理状态反馈 + /// - 日志记录:对应原始实现中的日志记录 + /// + /// 重构改进: + /// - 更统一的消息响应处理 + /// - 更清晰的错误处理 + /// - 更详细的日志记录 + /// - 保持了完全一致的处理逻辑 + /// + /// 接收到的消息,对应原始方法中的msg参数 + /// 错误处理回调,对应原始方法中的错误处理逻辑 + /// 是否成功处理,新增返回值提供处理状态反馈 + public bool HandleReceivedMessage(JObject message, Action? errorHandler = null) + { + ThrowIfDisposed(); + + if (message == null) + return false; + + try + { + // 使用MessageIdManager处理消息响应 - 替代原始的消息处理器查找逻辑 + var handled = _messageIdManager.HandleMessageResponse(message, errorHandler); + + if (handled) + { + _logger.LogDebug($"[{_clientName}] 消息已处理: message_id={message["message_id"]}"); + return true; + } + + // 处理特定消息类型 - 对应原始实现中的特定消息类型处理 + // 注意:这里不处理log_get和stats等业务逻辑,因为重构版本专注于消息传输 + var name = message["message"]?.ToString(); + if (!string.IsNullOrEmpty(name)) + { + _logger.LogDebug($"[{_clientName}] 未处理的特定消息类型: {name}"); + } + + return false; + } + catch (Exception ex) + { + _logger.LogError(ex, $"[{_clientName}] 处理消息异常: {ex.Message}"); + errorHandler?.Invoke($"消息处理错误: {ex.Message}"); + return false; + } + } + + /// + /// 设置消息处理器 - 对应LTEClientWebSocket.SetMessageHandler()方法 + /// + /// 功能说明: + /// 1. 设置按名称的消息处理器,对应原始SetMessageHandler()方法的核心逻辑 + /// 2. 委托给MessageIdManager处理,提供统一的消息处理器管理 + /// 3. 支持多个消息名称的处理器设置 + /// + /// 详细对应关系: + /// - 参数names:对应原始方法中的names参数,消息名称数组 + /// - 参数handler:对应原始方法中的handler参数,消息处理器 + /// - 处理器注册:对应原始的_messageHandlersByName注册逻辑 + /// - 日志记录:对应原始实现中的日志记录 + /// + /// 重构改进: + /// - 委托给MessageIdManager,提供统一管理 + /// - 保持了完全一致的接口和功能 + /// - 更好的错误处理和参数验证 + /// + /// 消息名称数组,对应原始方法中的names参数 + /// 消息处理器,对应原始方法中的handler参数 + public void SetMessageHandler(string[] names, MessageHandler handler) + { + ThrowIfDisposed(); + _messageIdManager.SetMessageHandler(names, handler); + } + + /// + /// 取消设置消息处理器 - 对应LTEClientWebSocket.UnsetMessageHandler()方法 + /// + /// 功能说明: + /// 1. 取消按名称的消息处理器,对应原始UnsetMessageHandler()方法的核心逻辑 + /// 2. 委托给MessageIdManager处理,提供统一的消息处理器管理 + /// 3. 支持多个消息名称的处理器取消 + /// + /// 详细对应关系: + /// - 参数names:对应原始方法中的names参数,消息名称数组 + /// - 处理器移除:对应原始的_messageHandlersByName移除逻辑 + /// - 日志记录:对应原始实现中的日志记录 + /// + /// 重构改进: + /// - 委托给MessageIdManager,提供统一管理 + /// - 保持了完全一致的接口和功能 + /// - 更好的错误处理和参数验证 + /// + /// 消息名称数组,对应原始方法中的names参数 + public void UnsetMessageHandler(string[] names) + { + ThrowIfDisposed(); + _messageIdManager.UnsetMessageHandler(names); + } + + /// + /// 检查是否为当前日志获取消息 - 对应LTEClientWebSocket中的_logGetId检查逻辑 + /// + /// 功能说明: + /// 1. 检查指定的消息ID是否为当前的日志获取消息ID + /// 2. 委托给MessageIdManager处理,提供统一的LogGet ID管理 + /// 3. 用于日志获取流程的状态检查 + /// + /// 详细对应关系: + /// - 参数messageId:对应原始实现中的消息ID检查 + /// - 返回值:true表示是当前LogGet消息,false表示不是,对应原始逻辑 + /// - 检查逻辑:对应原始的_logGetId比较逻辑 + /// + /// 重构改进: + /// - 委托给MessageIdManager,提供统一管理 + /// - 保持了完全一致的检查逻辑 + /// - 更好的线程安全性 + /// + /// 要检查的消息ID + /// 是否为当前日志获取消息 + public bool IsCurrentLogGetMessage(long messageId) + { + ThrowIfDisposed(); + return _messageIdManager.IsCurrentLogGetMessage(messageId); + } + + /// + /// 重置日志获取ID - 对应LTEClientWebSocket中的_logGetId重置逻辑 + /// + /// 功能说明: + /// 1. 重置日志获取消息ID,对应原始实现中的_logGetId重置逻辑 + /// 2. 委托给MessageIdManager处理,提供统一的LogGet ID管理 + /// 3. 用于日志获取流程的重置操作 + /// + /// 详细对应关系: + /// - 重置逻辑:对应原始的_logGetId = -1操作 + /// - 日志记录:对应原始实现中的日志记录 + /// - 事件触发:对应原始实现中的状态变化通知 + /// + /// 重构改进: + /// - 委托给MessageIdManager,提供统一管理 + /// - 保持了完全一致的重置逻辑 + /// - 更好的事件通知机制 + /// + public void ResetLogGetId() + { + ThrowIfDisposed(); + _messageIdManager.ResetLogGetId(); + } + + /// + /// 清理过期的消息处理器 - 对应LTEClientWebSocket中的处理器清理逻辑 + /// + /// 功能说明: + /// 1. 清理过期的消息处理器,防止内存泄漏 + /// 2. 委托给MessageIdManager处理,提供统一的处理器管理 + /// 3. 支持可配置的过期时间 + /// + /// 详细对应关系: + /// - 参数maxAge:对应原始实现中的过期时间配置 + /// - 清理逻辑:对应原始的处理器清理逻辑 + /// - 日志记录:对应原始实现中的日志记录 + /// + /// 重构改进: + /// - 委托给MessageIdManager,提供统一管理 + /// - 保持了完全一致的清理逻辑 + /// - 更好的内存管理 + /// + /// 最大存活时间(毫秒),默认30000毫秒 + public void CleanupExpiredHandlers(int maxAge = 30000) + { + ThrowIfDisposed(); + _messageIdManager.CleanupExpiredHandlers(maxAge); + } + + #endregion + } +} \ No newline at end of file diff --git a/CoreAgent.ProtocolClient/Managers/WebSocketMgr/WebSocketMessageManager.cs b/CoreAgent.ProtocolClient/Managers/WebSocketMgr/WebSocketMessageManager.cs new file mode 100644 index 0000000..0bef973 --- /dev/null +++ b/CoreAgent.ProtocolClient/Managers/WebSocketMgr/WebSocketMessageManager.cs @@ -0,0 +1,459 @@ +using Microsoft.Extensions.Logging; +using Newtonsoft.Json.Linq; +using Newtonsoft.Json; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using WebSocket4Net; + +namespace CoreAgent.ProtocolClient.Managers.WebSocketMgr +{ + /// + /// WebSocket消息管理器 - 专门处理WebSocket的收发业务 + /// + /// 重构说明: + /// 1. 对应LTEClientWebSocket中的WebSocket连接和消息传输功能 + /// 2. 集成MessageIdManager统一管理消息ID和回调 + /// 3. 移除_sentMessages和_receivedMessages消息缓存,专注传输 + /// 4. 移除业务逻辑功能(统计更新、日志解析等),实现职责分离 + /// + /// 主要功能: + /// - WebSocket连接管理(对应LTEClientWebSocket.Start()和Stop()) + /// - 消息发送和接收(对应LTEClientWebSocket.SendMessage()和OnSocketMessage()) + /// - 消息队列和批量发送(对应LTEClientWebSocket._messageFifo和SendMessageNow()) + /// - 事件通知(对应LTEClientWebSocket的事件系统) + /// + /// 与LTEClientWebSocket的详细对应关系: + /// + /// 1. 连接管理对应关系: + /// - Connect() 对应 Start() 方法中的WebSocket连接建立逻辑 + /// - Disconnect() 对应 Stop() 方法中的WebSocket关闭逻辑 + /// - OnSocketOpened/OnSocketClosed/OnSocketError 对应原始的事件处理器 + /// + /// 2. 消息发送对应关系: + /// - SendMessage() 对应 SendMessage() 方法的核心逻辑 + /// - SendLogGetMessage() 对应 LogGet() 方法中的消息发送部分 + /// - _messageFifo 对应原始的 _messageFifo 队列 + /// - SendMessageNow() 对应原始的 SendMessageNow() 方法 + /// - StartMessageDeferTimer() 对应原始的定时器启动逻辑 + /// + /// 3. 消息接收对应关系: + /// - OnSocketMessageReceived() 对应 OnSocketMessage() 方法 + /// - HandleReceivedMessage() 对应 OnSocketMessage() 中的消息处理逻辑 + /// - MessageReceived 事件对应原始的 MessageReceived 事件 + /// + /// 4. 消息ID管理对应关系: + /// - MessageIdManager 替代原始的 _messageId 和 _logGetId 字段 + /// - GenerateGeneralMessageId() 对应 Interlocked.Increment(ref _messageId) + /// - GenerateLogGetMessageId() 对应 _logGetId 的管理逻辑 + /// + /// 5. 事件系统对应关系: + /// - ConnectionOpened 对应原始的 ConnectionOpened 事件 + /// - ConnectionClosed 对应原始的 ConnectionClosed 事件 + /// - ConnectionError 对应原始的 ConnectionError 事件 + /// - MessageReceived 对应原始的 MessageReceived 事件 + /// - MessageSent 新增事件,提供更完整的消息生命周期通知 + /// + /// 6. 资源管理对应关系: + /// - Dispose() 对应原始的 Dispose() 方法 + /// - _disposed 字段对应原始的 _disposed 字段 + /// - 定时器管理对应原始的定时器清理逻辑 + /// + /// 重构优势: + /// 1. 职责分离:专注WebSocket传输,移除业务逻辑 + /// 2. 代码复用:可在多个地方复用WebSocket管理器 + /// 3. 测试友好:更容易进行单元测试 + /// 4. 维护简单:更清晰的代码结构 + /// 5. 功能增强:通过MessageIdManager提供更好的消息管理 + /// + public partial class WebSocketMessageManager : IDisposable + { + #region 私有字段 + + /// + /// WebSocket实例 - 对应LTEClientWebSocket._webSocket + /// 负责底层的WebSocket连接和通信 + /// + /// 对应关系: + /// - 创建:Connect()方法中创建,对应Start()方法中的_webSocket = new WebSocket(url) + /// - 配置:EnableAutoSendPing = false,对应原始实现 + /// - 事件绑定:绑定Opened/Closed/MessageReceived/Error事件,对应原始事件绑定 + /// - 连接:调用Open()方法,对应原始的_webSocket.Open() + /// - 关闭:调用Close()方法,对应原始的_webSocket.Close() + /// - 清理:在Disconnect()中设置为null,对应原始的资源清理 + /// + private WebSocket? _webSocket; + + /// + /// 消息ID管理器 - 对应LTEClientWebSocket._messageId和_logGetId + /// 统一管理通用消息ID和日志获取消息ID,提供更好的消息跟踪和回调管理 + /// + /// 对应关系: + /// - _messageId 对应 MessageIdManager.GenerateGeneralMessageId() + /// - _logGetId 对应 MessageIdManager.GenerateLogGetMessageId() + /// - _messageHandlers 对应 MessageIdManager的内部处理器管理 + /// - _messageHandlersByName 对应 MessageIdManager的按名称处理器管理 + /// + /// 功能增强: + /// - 线程安全的ID生成,替代原始的Interlocked.Increment + /// - 统一的处理器管理,提供更好的回调跟踪 + /// - 自动清理过期处理器,防止内存泄漏 + /// - 事件通知机制,提供ID变化通知 + /// + private readonly MessageIdManager _messageIdManager; + + /// + /// 日志记录器 - 对应LTEClientWebSocket._logger + /// 用于记录WebSocket操作和错误信息 + /// + /// 对应关系: + /// - 构造函数参数:对应LTEClientWebSocket构造函数中的logger参数 + /// - 日志记录:对应原始实现中的所有_logger.LogXXX调用 + /// - 日志格式:保持与原始实现一致的日志格式 + /// + /// 功能增强: + /// - 更详细的错误日志记录 + /// - 更好的异常堆栈跟踪 + /// - 统一的日志格式和级别 + /// + private readonly ILogger _logger; + + /// + /// 客户端名称 - 对应LTEClientWebSocket._config.Name + /// 用于日志记录和事件标识 + /// + /// 对应关系: + /// - 构造函数参数:对应LTEClientWebSocket构造函数中的config.Name + /// - 日志前缀:对应原始实现中所有日志的[{_config.Name}]前缀 + /// - 事件标识:用于区分不同客户端的事件 + /// + /// 功能增强: + /// - 参数验证:确保clientName不为null + /// - 统一标识:在所有日志和事件中使用一致的客户端标识 + /// + private readonly string _clientName; + + /// + /// 消息队列 - 对应LTEClientWebSocket._messageFifo + /// 线程安全的阻塞集合,用于批量发送优化 + /// 优化说明:从ConcurrentQueue改为BlockingCollection,提供更好的线程安全性和阻塞能力 + /// + /// 对应关系: + /// - 队列类型:BlockingCollection,优化后的线程安全集合 + /// - 入队操作:Add(message),对应原始的_messageFifo.Enqueue(message) + /// - 出队操作:TryTake(out message),对应原始的队列处理逻辑 + /// - 批量处理:支持批量消息发送,对应原始的批处理逻辑 + /// + /// 功能增强: + /// - 线程安全:使用BlockingCollection保证线程安全 + /// - 阻塞能力:支持阻塞式出队操作,提高性能 + /// - 批量优化:支持批量发送减少网络开销 + /// - 延迟发送:配合_messageDeferTimer实现延迟发送 + /// - 资源管理:自动处理集合的完成状态 + /// + /// 重构改进: + /// - 移除了消息缓存功能,专注传输 + /// - 优化了队列操作逻辑,提供更好的性能 + /// - 增强了线程安全性和资源管理 + /// + private readonly BlockingCollection _messageFifo; + + /// + /// 消息延迟发送定时器 - 对应LTEClientWebSocket._messageDeferTimer + /// 用于实现消息的批量发送和延迟发送机制 + /// 保持与原始实现完全一致的逻辑 + /// + /// 对应关系: + /// - 定时器类型:Timer,与原始实现完全一致 + /// - 启动逻辑:StartMessageDeferTimer(),对应原始的定时器启动 + /// - 停止逻辑:StopMessageDeferTimer(),对应原始的定时器停止 + /// - 延迟策略:1毫秒延迟,与原始实现完全一致 + /// - 批处理大小:100条消息,与原始实现完全一致 + /// + /// 功能保持: + /// - 批量发送:当队列中消息少于100条时,延迟1毫秒发送 + /// - 立即发送:当队列中消息达到100条时,立即发送 + /// - 资源管理:在Disconnect()和Dispose()中正确释放 + /// + /// 重构改进: + /// - 更清晰的定时器管理逻辑 + /// - 更好的异常处理 + /// - 保持了完全一致的批处理策略 + /// + private Timer? _messageDeferTimer; + + /// + /// 释放标志 - 对应LTEClientWebSocket._disposed + /// 防止重复释放和已释放对象的操作 + /// + /// 对应关系: + /// - 字段类型:bool,对应原始的_disposed字段 + /// - 返回值:true表示已释放,false表示未释放,对应原始实现 + /// - 使用场景:外部检查对象释放状态,对应原始实现 + /// - 线程安全:直接返回_disposed字段值,对应原始实现 + /// + /// 功能保持: + /// - 释放状态检查:外部可以检查对象是否已释放 + /// - 资源保护:防止对已释放对象的操作 + /// - 状态查询:提供对象状态的查询接口 + /// + /// 重构改进: + /// - 保持了完全一致的检查逻辑 + /// - 保持了完全一致的返回值语义 + /// - 保持了完全一致的使用场景 + /// + private bool _disposed; + + /// + /// 同步锁对象 + /// 用于确保线程安全的操作 + /// + /// 对应关系: + /// - 新增功能:原始实现中没有显式的同步锁 + /// - 用途:确保关键操作的线程安全 + /// - 使用场景:在需要线程安全的地方使用lock语句 + /// + /// 功能增强: + /// - 线程安全:确保关键操作的原子性 + /// - 死锁预防:使用细粒度锁避免死锁 + /// - 性能优化:最小化锁的持有时间 + /// + private readonly object _lockObject = new object(); + + #endregion + + #region 事件 + + /// + /// 连接打开事件 - 对应LTEClientWebSocket.ConnectionOpened + /// 当WebSocket连接成功建立时触发 + /// + /// 对应关系: + /// - 事件类型:EventHandler,与原始实现完全一致 + /// - 触发时机:在OnSocketOpened()中触发,对应原始的OnSocketOpened事件处理 + /// - 触发条件:WebSocket连接成功建立时 + /// - 事件参数:无参数,与原始实现完全一致 + /// + /// 功能保持: + /// - 连接状态通知:通知外部连接已建立 + /// - 事件订阅:支持多个订阅者 + /// - 异步触发:事件触发不会阻塞WebSocket操作 + /// + /// 重构改进: + /// - 更清晰的触发时机 + /// - 更好的错误处理 + /// - 保持了完全一致的事件接口 + /// + public event EventHandler? ConnectionOpened; + + /// + /// 连接关闭事件 - 对应LTEClientWebSocket.ConnectionClosed + /// 当WebSocket连接关闭时触发 + /// + /// 对应关系: + /// - 事件类型:EventHandler,与原始实现完全一致 + /// - 触发时机:在OnSocketClosed()中触发,对应原始的OnSocketClosed事件处理 + /// - 触发条件:WebSocket连接关闭时 + /// - 事件参数:无参数,与原始实现完全一致 + /// + /// 功能保持: + /// - 连接状态通知:通知外部连接已关闭 + /// - 事件订阅:支持多个订阅者 + /// - 异步触发:事件触发不会阻塞WebSocket操作 + /// + /// 重构改进: + /// - 更清晰的触发时机 + /// - 更好的资源清理 + /// - 保持了完全一致的事件接口 + /// + public event EventHandler? ConnectionClosed; + + /// + /// 连接错误事件 - 对应LTEClientWebSocket.ConnectionError + /// 当WebSocket连接发生错误时触发 + /// + /// 对应关系: + /// - 事件类型:EventHandler,与原始实现完全一致 + /// - 触发时机:在OnSocketError()和异常处理中触发,对应原始的错误处理 + /// - 触发条件:WebSocket连接错误、消息处理错误等 + /// - 事件参数:错误信息字符串,与原始实现完全一致 + /// + /// 功能保持: + /// - 错误通知:通知外部连接或处理错误 + /// - 事件订阅:支持多个订阅者 + /// - 异步触发:事件触发不会阻塞WebSocket操作 + /// + /// 重构改进: + /// - 更详细的错误信息 + /// - 更好的异常处理 + /// - 保持了完全一致的事件接口 + /// + public event EventHandler? ConnectionError; + + /// + /// 消息接收事件 - 对应LTEClientWebSocket.MessageReceived + /// 当接收到WebSocket消息时触发 + /// + /// 对应关系: + /// - 事件类型:EventHandler,与原始实现完全一致 + /// - 触发时机:在OnSocketMessageReceived()中触发,对应原始的OnSocketMessage事件处理 + /// - 触发条件:接收到WebSocket消息并解析成功后 + /// - 事件参数:解析后的JObject消息,与原始实现完全一致 + /// + /// 功能保持: + /// - 消息通知:通知外部接收到新消息 + /// - 事件订阅:支持多个订阅者 + /// - 异步触发:事件触发不会阻塞消息处理 + /// - 触发顺序:在消息处理开始时就触发,与原始实现完全一致 + /// + /// 重构改进: + /// - 更清晰的触发时机 + /// - 更好的消息解析 + /// - 保持了完全一致的事件接口和触发顺序 + /// + public event EventHandler? MessageReceived; + + /// + /// 消息发送事件 - 新增功能,LTEClientWebSocket中没有对应事件 + /// 当消息成功发送时触发,提供更完整的消息生命周期通知 + /// + /// 对应关系: + /// - 事件类型:EventHandler,与MessageReceived保持一致 + /// - 触发时机:在SendMessageNow()中触发,对应消息发送成功时 + /// - 触发条件:消息成功发送到WebSocket时 + /// - 事件参数:发送的JObject消息,与MessageReceived保持一致 + /// + /// 功能增强: + /// - 消息生命周期:提供完整的消息发送通知 + /// - 调试支持:便于调试消息发送流程 + /// - 监控支持:便于监控消息发送状态 + /// - 事件订阅:支持多个订阅者 + /// + /// 重构优势: + /// - 更完整的消息生命周期管理 + /// - 更好的调试和监控支持 + /// - 与MessageReceived事件形成对称的事件系统 + /// + public event EventHandler? MessageSent; + + #endregion + + #region 属性 + + /// + /// 是否已连接 - 对应LTEClientWebSocket.IsConnected + /// 检查WebSocket连接状态 + /// + /// 对应关系: + /// - 属性类型:bool,与原始实现完全一致 + /// - 检查逻辑:_webSocket?.State == WebSocketState.Open,与原始实现完全一致 + /// - 使用场景:在SendMessage()中检查连接状态,对应原始实现 + /// - 返回值:true表示已连接,false表示未连接,与原始实现完全一致 + /// + /// 功能保持: + /// - 连接状态检查:快速检查WebSocket连接状态 + /// - 空安全:使用?.操作符避免空引用异常 + /// - 实时状态:反映WebSocket的实时连接状态 + /// + /// 重构改进: + /// - 保持了完全一致的检查逻辑 + /// - 保持了完全一致的返回值语义 + /// - 保持了完全一致的使用场景 + /// + public bool IsConnected => _webSocket?.State == WebSocketState.Open; + + /// + /// WebSocket状态 - 对应LTEClientWebSocket._webSocket?.State + /// 获取详细的WebSocket连接状态 + /// + /// 对应关系: + /// - 属性类型:WebSocketState,对应原始的_webSocket?.State + /// - 返回值:WebSocket的详细状态,对应原始实现 + /// - 空安全:使用??操作符提供默认值,对应原始实现 + /// - 使用场景:提供更详细的连接状态信息 + /// + /// 功能保持: + /// - 详细状态:提供WebSocket的详细连接状态 + /// - 空安全:当_webSocket为null时返回WebSocketState.None + /// - 实时状态:反映WebSocket的实时状态 + /// + /// 重构改进: + /// - 保持了完全一致的状态获取逻辑 + /// - 保持了完全一致的默认值处理 + /// - 保持了完全一致的使用场景 + /// + public WebSocketState State => _webSocket?.State ?? WebSocketState.None; + + /// + /// 是否已释放 - 对应LTEClientWebSocket._disposed + /// 检查对象是否已被释放 + /// + /// 对应关系: + /// - 属性类型:bool,对应原始的_disposed字段 + /// - 返回值:true表示已释放,false表示未释放,对应原始实现 + /// - 使用场景:外部检查对象释放状态,对应原始实现 + /// - 线程安全:直接返回_disposed字段值,对应原始实现 + /// + /// 功能保持: + /// - 释放状态检查:外部可以检查对象是否已释放 + /// - 资源保护:防止对已释放对象的操作 + /// - 状态查询:提供对象状态的查询接口 + /// + /// 重构改进: + /// - 保持了完全一致的检查逻辑 + /// - 保持了完全一致的返回值语义 + /// - 保持了完全一致的使用场景 + /// + public bool IsDisposed => _disposed; + + /// + /// 消息队列数量 - 对应LTEClientWebSocket._messageFifo.Count + /// 获取当前待发送消息的数量 + /// + /// 对应关系: + /// - 属性类型:int,对应原始的_messageFifo.Count + /// - 返回值:队列中待发送消息的数量,对应原始实现 + /// - 使用场景:监控消息队列状态,对应原始实现 + /// - 线程安全:BlockingCollection.Count是线程安全的,对应原始实现 + /// + /// 功能保持: + /// - 队列监控:监控当前待发送消息的数量 + /// - 性能监控:便于监控消息发送性能 + /// - 调试支持:便于调试消息队列状态 + /// + /// 重构改进: + /// - 保持了完全一致的计数逻辑 + /// - 保持了完全一致的返回值语义 + /// - 保持了完全一致的使用场景 + /// - 优化:使用BlockingCollection提供更好的线程安全性 + /// + public int MessageQueueCount => _messageFifo.Count; + + /// + /// 消息ID管理器 - 提供对MessageIdManager的访问 + /// 允许外部访问消息ID管理功能 + /// + /// 对应关系: + /// - 属性类型:MessageIdManager,对应原始的_messageId和_logGetId管理 + /// - 返回值:内部的消息ID管理器实例,对应原始实现 + /// - 使用场景:外部访问消息ID管理功能,对应原始实现 + /// - 封装性:提供对内部MessageIdManager的访问,对应原始实现 + /// + /// 功能保持: + /// - 功能访问:外部可以访问消息ID管理功能 + /// - 封装性:保持内部实现的封装性 + /// - 扩展性:支持外部扩展消息ID管理功能 + /// + /// 重构改进: + /// - 更统一的消息ID管理接口 + /// - 更好的功能封装 + /// - 保持了完全一致的功能访问方式 + /// + public MessageIdManager MessageIdManager => _messageIdManager; + + #endregion + } +} \ No newline at end of file diff --git a/CoreAgent.ProtocolClient/Managers/WebSocketMgr/结构层次.md b/CoreAgent.ProtocolClient/Managers/WebSocketMgr/结构层次.md new file mode 100644 index 0000000..dee7b55 --- /dev/null +++ b/CoreAgent.ProtocolClient/Managers/WebSocketMgr/结构层次.md @@ -0,0 +1,35 @@ +# WebSocketMessageManager 结构层次说明 + +``` +Core/WebSocket/Managers/WebSocketMessageManager/ +├── WebSocketMessageManager.cs # 主体声明:类声明、字段、属性、事件 +├── Constructor.cs # 构造函数:初始化相关 +├── PublicMethods.cs # 公共方法:对外接口、业务主流程 +├── PrivateMethods.cs # 私有方法:内部逻辑、事件处理器、辅助方法 +├── Dispose.cs # IDisposable实现:资源释放、清理 +``` + +## 各文件职责说明 + +- **WebSocketMessageManager.cs** + - 声明类本体(partial class),包含所有字段、属性、事件声明。 + - 只负责结构性声明,不含具体实现。 + +- **Constructor.cs** + - 只包含构造函数,负责对象初始化、依赖注入、字段赋值。 + +- **PublicMethods.cs** + - 所有对外公开的方法(如Connect、Disconnect、SendMessage等)。 + - 业务主流程、外部接口全部集中于此,便于查找和维护。 + +- **PrivateMethods.cs** + - 所有私有方法、事件处理器、内部辅助逻辑。 + - 包括定时器、消息分发、内部校验等。 + +- **Dispose.cs** + - 只包含IDisposable接口实现和资源释放相关方法。 + - 负责对象生命周期的正确终结。 + +--- + +> 本结构层次仅为物理文件组织优化,**所有业务逻辑、接口、注释、实现细节均与原始文件完全一致**,仅提升可维护性和可读性。 \ No newline at end of file diff --git a/CoreAgent.ProtocolClient/Models/CellConfig.cs b/CoreAgent.ProtocolClient/Models/CellConfig.cs new file mode 100644 index 0000000..7e82016 --- /dev/null +++ b/CoreAgent.ProtocolClient/Models/CellConfig.cs @@ -0,0 +1,222 @@ +using System; +using System.Collections.Generic; +using Newtonsoft.Json; + +namespace CoreAgent.ProtocolClient.Models +{ + /// + /// 小区配置实体,对应无线参数配置 + /// 用于存储LTE小区的各种物理层和协议层配置参数 + /// 支持JSON序列化,属性名与外部API保持一致 + /// + public class CellConfig + { + /// 下行天线数量 + [JsonProperty("n_antenna_dl")] + public int NAntennaDl { get; set; } + + /// 上行天线数量 + [JsonProperty("n_antenna_ul")] + public int NAntennaUl { get; set; } + + /// 下行传输层数 + [JsonProperty("n_layer_dl")] + public int NLayerDl { get; set; } + + /// 上行传输层数 + [JsonProperty("n_layer_ul")] + public int NLayerUl { get; set; } + + /// 天线增益(dB) + [JsonProperty("gain")] + public int Gain { get; set; } + + /// 上行链路是否禁用 + [JsonProperty("ul_disabled")] + public bool UlDisabled { get; set; } + + /// 射频端口号 + [JsonProperty("rf_port")] + public int RfPort { get; set; } + + /// 下行QAM调制阶数 + [JsonProperty("dl_qam")] + public int DlQam { get; set; } + + /// 上行QAM调制阶数 + [JsonProperty("ul_qam")] + public int UlQam { get; set; } + + /// 物理小区标识(PCI) + [JsonProperty("n_id_cell")] + public int NIdCell { get; set; } + + /// 下行资源块数量 + [JsonProperty("n_rb_dl")] + public int NRbDl { get; set; } + + /// 上行资源块数量 + [JsonProperty("n_rb_ul")] + public int NRbUl { get; set; } + + /// 下行E-UTRA绝对射频信道号 + [JsonProperty("dl_earfcn")] + public int DlEarfcn { get; set; } + + /// 上行E-UTRA绝对射频信道号 + [JsonProperty("ul_earfcn")] + public int UlEarfcn { get; set; } + + /// LTE频段号 + [JsonProperty("band")] + public int Band { get; set; } + + /// 下行载波频率(Hz) + [JsonProperty("dl_freq")] + public long DlFreq { get; set; } + + /// 上行载波频率(Hz) + [JsonProperty("ul_freq")] + public long UlFreq { get; set; } + + /// 双工模式(FDD/TDD) + [JsonProperty("mode")] + public string Mode { get; set; } = string.Empty; + + /// PRACH序列索引 + [JsonProperty("prach_sequence_index")] + public int PrachSequenceIndex { get; set; } + + /// 下行循环前缀类型 + [JsonProperty("dl_cyclic_prefix")] + public string DlCyclicPrefix { get; set; } = string.Empty; + + /// 上行循环前缀类型 + [JsonProperty("ul_cyclic_prefix")] + public string UlCyclicPrefix { get; set; } = string.Empty; + + /// PRACH配置索引 + [JsonProperty("prach_config_index")] + public int PrachConfigIndex { get; set; } + + /// PRACH频域偏移 + [JsonProperty("prach_freq_offset")] + public int PrachFreqOffset { get; set; } + + /// PUCCH delta shift参数 + [JsonProperty("delta_pucch_shift")] + public int DeltaPucchShift { get; set; } + + /// CQI报告的资源块数量 + [JsonProperty("n_rb_cqi")] + public int NRbCqi { get; set; } + + /// 循环移位天线端口数量 + [JsonProperty("n_cs_an")] + public int NCsAn { get; set; } + + /// PUCCH资源配置列表 + [JsonProperty("pucch_allocation")] + public List PucchAllocation { get; set; } = new(); + + /// PUCCH ACK/NACK起始位置 + [JsonProperty("pucch_ack_nack_start")] + public int PucchAckNackStart { get; set; } + + /// PUCCH保留资源块列表 + [JsonProperty("pucch_reserved_rbs")] + public List PucchReservedRbs { get; set; } = new(); + + /// 调度请求(SR)资源数量 + [JsonProperty("sr_resource_count")] + public int SrResourceCount { get; set; } + + /// CQI资源数量 + [JsonProperty("cqi_resource_count")] + public int CqiResourceCount { get; set; } + + /// SRS资源配置 + [JsonProperty("srs_resources")] + public SrsResources SrsResources { get; set; } = new(); + + /// 保证比特率(GBR)配置 + [JsonProperty("gbr")] + public GbrConfig Gbr { get; set; } = new(); + + /// 跟踪区域码(TAC) + [JsonProperty("tac")] + public int Tac { get; set; } + + /// 公共陆地移动网络(PLMN)列表 + [JsonProperty("plmn_list")] + public List PlmnList { get; set; } = new(); + } + + /// + /// PUCCH资源配置结构 + /// 定义物理上行控制信道的资源配置参数 + /// + public class PucchAllocation + { + /// PUCCH格式类型 + [JsonProperty("type")] + public string Type { get; set; } = string.Empty; + + /// 分配的资源块数量 + [JsonProperty("rbs")] + public int Rbs { get; set; } + + /// PUCCH参数n + [JsonProperty("n")] + public int N { get; set; } + } + + /// + /// SRS资源配置结构 + /// 定义探测参考信号的资源配置参数 + /// + public class SrsResources + { + /// 频域偏移 + [JsonProperty("offsets")] + public int Offsets { get; set; } + + /// 频点数量 + [JsonProperty("freqs")] + public int Freqs { get; set; } + + /// 总资源数量 + [JsonProperty("total")] + public int Total { get; set; } + } + + /// + /// 保证比特率(GBR)配置结构 + /// 定义服务质量相关的比特率限制 + /// + public class GbrConfig + { + /// 下行速率限制(bps) + [JsonProperty("dl_limit")] + public int DlLimit { get; set; } + + /// 上行速率限制(bps) + [JsonProperty("ul_limit")] + public int UlLimit { get; set; } + } + + /// + /// PLMN配置项 + /// 定义公共陆地移动网络的配置信息 + /// + public class PlmnItem + { + /// PLMN编码(MCC+MNC) + [JsonProperty("plmn")] + public string Plmn { get; set; } = string.Empty; + + /// 是否为保留PLMN + [JsonProperty("reserved")] + public bool Reserved { get; set; } + } +} \ No newline at end of file diff --git a/CoreAgent.ProtocolClient/Models/ClientConfig.cs b/CoreAgent.ProtocolClient/Models/ClientConfig.cs new file mode 100644 index 0000000..f2f3980 --- /dev/null +++ b/CoreAgent.ProtocolClient/Models/ClientConfig.cs @@ -0,0 +1,112 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CoreAgent.ProtocolClient.Models +{ + + /// + /// 客户端配置 + /// + public class ClientConfig + { + /// + /// 客户端名称 + /// + public string Name { get; set; } = string.Empty; + + /// + /// 服务器地址 + /// + public string Address { get; set; } = string.Empty; + + /// + /// 是否启用 + /// + public bool Enabled { get; set; } + + /// + /// 密码 + /// + public string Password { get; set; } = string.Empty; + + /// + /// 重连延迟(毫秒) + /// + public int ReconnectDelay { get; set; } = 5000; + + /// + /// 是否启用SSL + /// + public bool Ssl { get; set; } + + /// + /// 日志配置 + /// + public ClientLogsConfig Logs { get; set; } = new(); + + /// + /// 是否只读 + /// + public bool Readonly { get; set; } + + + /// + /// 模型 + /// + public string Model { get; set; } + } + + /// + /// 客户端日志配置 + /// + public class ClientLogsConfig + { + /// + /// 日志层配置 + /// + public Dictionary Layers { get; set; } = new(); + + /// + /// 是否启用信号日志 + /// + public bool? Signal { get; set; } + + /// + /// 是否启用控制信道日志 + /// + public bool? Cch { get; set; } + } + + + /// + /// 日志层配置 + /// + public class LogLayerConfig + { + /// + /// 日志级别 + /// + [Required] + public string Level { get; set; } = "warn"; + + /// + /// 最大大小 + /// + public int MaxSize { get; set; } = 1; + + /// + /// 是否包含负载 + /// + public bool Payload { get; set; } = false; + + /// + /// 过滤器(用于兼容性) + /// + public string Filter { get; set; } = "warn"; + + } +} diff --git a/CoreAgent.ProtocolClient/Models/LogLayerHelp.cs b/CoreAgent.ProtocolClient/Models/LogLayerHelp.cs new file mode 100644 index 0000000..027f127 --- /dev/null +++ b/CoreAgent.ProtocolClient/Models/LogLayerHelp.cs @@ -0,0 +1,60 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CoreAgent.ProtocolClient.Models +{ + /// + /// 日志层配置帮助类 + /// 提供预定义的日志层配置,用于不同协议层的日志记录控制 + /// 支持LTE协议栈各层的日志级别、大小限制和过滤规则配置 + /// + public static class LogLayerHelp + { + /// + /// 获取默认的LTE协议层日志配置 + /// 包含PHY、MAC、RLC、PDCP、RRC、NAS等各层的预定义配置 + /// + /// 协议层名称到配置的映射字典 + public static Dictionary GetDefaultCustomLayerConfigs() + { + // 直接返回静态配置,避免不必要的嵌套和动态解析 + return new Dictionary + { + ["PHY"] = new LogLayerConfig { Level = "warn", MaxSize = 1000, Payload = true, Filter = "info" }, + ["MAC"] = new LogLayerConfig { Level = "warn", MaxSize = 1000, Payload = true, Filter = "info" }, + ["RLC"] = new LogLayerConfig { Level = "info", MaxSize = 1000, Payload = false, Filter = "info" }, + ["PDCP"] = new LogLayerConfig { Level = "warn", MaxSize = 1000, Payload = false, Filter = "warn" }, + ["RRC"] = new LogLayerConfig { Level = "debug", MaxSize = 1000, Payload = true, Filter = "debug" }, + ["NAS"] = new LogLayerConfig { Level = "debug", MaxSize = 1000, Payload = true, Filter = "debug" }, + ["S1AP"] = new LogLayerConfig { Level = "warn", MaxSize = 1000, Payload = false, Filter = "info" }, + ["NGAP"] = new LogLayerConfig { Level = "warn", MaxSize = 1000, Payload = false, Filter = "info" }, + ["GTPU"] = new LogLayerConfig { Level = "info", MaxSize = 1000, Payload = false, Filter = "info" }, + ["X2AP"] = new LogLayerConfig { Level = "warn", MaxSize = 1000, Payload = false, Filter = "info" }, + ["XnAP"] = new LogLayerConfig { Level = "info", MaxSize = 1000, Payload = false, Filter = "info" }, + ["M2AP"] = new LogLayerConfig { Level = "info", MaxSize = 1000, Payload = false, Filter = "info" } + }; + } + + /// + /// 获取IMS协议层的日志配置 + /// 包含IMS、CX、RX、SIP、MEDIA、MMS等IMS相关协议的配置 + /// + /// IMS协议层名称到配置的映射字典 + public static Dictionary GetIMSCustomLayerConfigs() + { + // 直接返回静态配置,避免不必要的嵌套和动态解析 + return new Dictionary + { + ["IMS"] = new LogLayerConfig { Level = "warn", MaxSize = 1000, Payload = true, Filter = "debug" }, + ["CX"] = new LogLayerConfig { Level = "warn", MaxSize = 1000, Payload = true, Filter = "debug" }, + ["RX"] = new LogLayerConfig { Level = "warn", MaxSize = 1000, Payload = false, Filter = "debug" }, + ["SIP"] = new LogLayerConfig { Level = "debug", MaxSize = 1000, Payload = false, Filter = "debug" }, + ["MEDIA"] = new LogLayerConfig { Level = "warn", MaxSize = 1000, Payload = true, Filter = "debug" }, + ["MMS"] = new LogLayerConfig { Level = "warn", MaxSize = 1000, Payload = true, Filter = "debug" }, + }; + } + } +} diff --git a/CoreAgent.ProtocolClient/Models/MessageHandler.cs b/CoreAgent.ProtocolClient/Models/MessageHandler.cs new file mode 100644 index 0000000..02f8cc5 --- /dev/null +++ b/CoreAgent.ProtocolClient/Models/MessageHandler.cs @@ -0,0 +1,36 @@ +using Newtonsoft.Json.Linq; + +namespace CoreAgent.ProtocolClient.Models +{ + /// + /// 消息处理器模型 + /// 用于处理WebSocket消息的回调和错误处理 + /// 支持不同类型的消息处理器,包括普通消息、错误消息和日志获取消息 + /// + public class MessageHandler + { + /// + /// 消息处理回调函数 + /// 当接收到消息时会被调用的委托函数 + /// + public Action? Callback { get; set; } + + /// + /// 标识是否为错误处理器 + /// 当为true时,该处理器专门用于处理错误消息 + /// + public bool ErrorHandler { get; set; } + + /// + /// 处理器创建时间 + /// 用于跟踪处理器的生命周期和超时管理 + /// + public DateTime CreatedAt { get; set; } = DateTime.UtcNow; + + /// + /// 标识是否为日志获取处理器 + /// 当为true时,该处理器专门用于处理日志获取相关的消息 + /// + public bool IsLogGetHandler { get; set; } + } +} \ No newline at end of file diff --git a/CoreAgent.ProtocolClient/Models/ProtocolCaps.cs b/CoreAgent.ProtocolClient/Models/ProtocolCaps.cs new file mode 100644 index 0000000..8782b6e --- /dev/null +++ b/CoreAgent.ProtocolClient/Models/ProtocolCaps.cs @@ -0,0 +1,111 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CoreAgent.ProtocolClient.Models +{ + /// + /// LTE协议能力信息模型 + /// 用于存储用户设备(UE)的LTE协议栈能力信息,包括频段支持、UE类别等 + /// + public class ProtocolCaps + { + /// + /// 用户设备唯一标识符 + /// + public int UeId { get; set; } + + /// + /// 支持的LTE频段列表 + /// + public List Bands { get; set; } = new List(); + + /// + /// UE类别(可选) + /// + public int? Category { get; set; } + + /// + /// 下行UE类别(可选) + /// + public int? CategoryDl { get; set; } + + /// + /// 上行UE类别(可选) + /// + public int? CategoryUl { get; set; } + + /// + /// 协议能力数据列表 + /// + public List Data { get; set; } = new List(); + + /// + /// 数据项计数 + /// + public int Count { get; set; } = 0; + + /// + /// 频段组合列表 + /// + public List BandComb { get; set; } = new List(); + + /// + /// ASN.1编码数据列表 + /// + public List Asn1 { get; set; } = new List(); + } + + /// + /// ProtocolCaps扩展方法类 + /// 提供ProtocolCaps相关的工具方法和扩展功能 + /// + public static class ProtocolCapsExtensions + { + /// + /// UE能力MIMO层数映射字典 + /// 定义不同MIMO配置对应的层数 + /// + private static readonly Dictionary UE_CAPS_MIMO = new Dictionary + { + { "twoLayers", 2 }, + { "fourLayers", 4 }, + { "eightLayers", 8 } + }; + + /// + /// 获取UE类别信息的字符串表示 + /// 优先返回DL/UL类别组合,如果没有则返回通用类别 + /// + /// 协议能力信息对象 + /// 格式化的类别信息字符串 + public static string GetCategory(this ProtocolCaps caps) + { + List categories = new List(); + + if (caps.CategoryDl.HasValue) + { + categories.Add($"DL={caps.CategoryDl.Value}"); + } + + if (caps.CategoryUl.HasValue) + { + categories.Add($"UL={caps.CategoryUl.Value}"); + } + + if (categories.Count > 0) + { + return string.Join(",", categories); + } + + if (caps.Category.HasValue) + { + return caps.Category.Value.ToString(); + } + + return string.Empty; + } + } +} diff --git a/CoreAgent.ProtocolClient/Models/ProtocolLog.cs b/CoreAgent.ProtocolClient/Models/ProtocolLog.cs new file mode 100644 index 0000000..50a536d --- /dev/null +++ b/CoreAgent.ProtocolClient/Models/ProtocolLog.cs @@ -0,0 +1,212 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading.Tasks; + +namespace CoreAgent.ProtocolClient.Models +{ + public class ProtocolLog + { + #region 基础属性 + /// + /// 日志唯一标识符 + /// + public int Id { get; set; } + /// + /// 时间戳(毫秒) + /// + public long Timestamp { get; set; } + /// + /// 协议层(PHY, RRC, NAS, MAC等) + /// + public string Layer { get; set; } = string.Empty; + /// + /// 传输方向(UL/DL) + /// + public int Direction { get; set; } + /// + /// 消息内容 + /// + public string Message { get; set; } = string.Empty; + /// + /// 消息信息类型 + /// + public int? Info { get; set; } + /// + /// UE标识符 + /// + public int? UeId { get; set; } + /// + /// RNTI(无线网络临时标识符) + /// + public int? Rnti { get; set; } + /// + /// 日志数据内容 + /// + public List Data { get; set; } = new(); + /// + /// 是否已解码 + /// + public bool Decoded { get; set; } + /// + /// 标记信息 + /// + public string? Marker { get; set; } + /// + /// SIP原始消息内容 + /// + public string? Msg0 { get; set; } + /// + /// 小区标识(所有层通用) + /// + public int? Cell { get; set; } + #endregion + + #region PHY层相关属性 + public PhyFields Phy { get; set; } = new(); + #endregion + + #region 数据相关属性 + public DataFields DataInfo { get; set; } = new(); + #endregion + + #region MAC层相关属性 + public MacFields Mac { get; set; } = new(); + #endregion + } + + // PHY层分组 + public class PhyFields + { + /// + /// 物理信道类型 + /// + public string? Channel { get; set; } + /// + /// 帧号 + /// + public int? Frame { get; set; } + /// + /// 子帧号 + /// + public int? Subframe { get; set; } + /// + /// 时隙号 + /// + public int? Slot { get; set; } + /// + /// 符号号 + /// + public int? Symbol { get; set; } + /// + /// 天线端口 + /// + public int? AntennaPort { get; set; } + /// + /// 资源块起始位置 + /// + public int? RbStart { get; set; } + /// + /// 资源块数量 + /// + public int? RbCount { get; set; } + /// + /// 调制编码方案 + /// + public int? Mcs { get; set; } + /// + /// 传输块大小 + /// + public int? Tbs { get; set; } + /// + /// HARQ进程ID + /// + public int? HarqId { get; set; } + /// + /// HARQ新数据指示 + /// + public bool? HarqNdi { get; set; } + /// + /// HARQ重传次数 + /// + public int? HarqRedundancyVersion { get; set; } + } + + // 数据相关分组 + public class DataFields + { + /// + /// IP长度 + /// + public int? IpLen { get; set; } + /// + /// SDU长度 + /// + public int? SduLen { get; set; } + /// + /// 链路ID + /// + public LinkIds? LinkIds { get; set; } + /// + /// 信号记录 + /// + public Dictionary? SignalRecord { get; set; } + } + + // MAC层分组 + public class MacFields + { + /// + /// 功率余量报告(PHR) + /// + public int? Phr { get; set; } + /// + /// 上行缓冲区大小 + /// + public int? UlBufferSize { get; set; } + /// + /// MAC填充长度 + /// + public int? MacPad { get; set; } + /// + /// MAC数据长度 + /// + public int? MacLen { get; set; } + /// + /// 定时提前量(TA) + /// + public int? Ta { get; set; } + } + + public class LinkIds + { + /// + /// Core链路ID + /// + public int? Core { get; set; } + /// + /// Ran链路ID + /// + public int? Ran { get; set; } + } + + + /// + /// ProtocolLog扩展方法类 + /// 提供ProtocolLog相关的工具方法和扩展功能 + /// + public static class ProtocolLogExtensions + { + /// + /// 获取协议日志数据的字符串表示 + /// + /// 协议日志对象 + /// 格式化的日志数据字符串 + public static string GetProtocolLogData(this ProtocolLog log) + { + return string.Join("\n", log.Data); + } + } +} diff --git a/CoreAgent.ProtocolClient/Models/ProtocolLogDetail.cs b/CoreAgent.ProtocolClient/Models/ProtocolLogDetail.cs new file mode 100644 index 0000000..895a304 --- /dev/null +++ b/CoreAgent.ProtocolClient/Models/ProtocolLogDetail.cs @@ -0,0 +1,117 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using CoreAgent.ProtocolClient.Enums; + +namespace CoreAgent.ProtocolClient.Models +{ + /// + /// 协议日志详情实体 + /// 用于存储解析后的协议日志详细信息到数据库 + /// + [Table("ProtocolLogDetails")] + public class ProtocolLogDetail + { + /// + /// 主键ID + /// + [Key] + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public long Id { get; set; } + + /// + /// 协议层类型 + /// + [Required] + [Column("LayerType")] + public ProtocolLayer LayerType { get; set; } + + /// + /// 消息详情集合(JSON格式存储) + /// + [Column("MessageDetail", TypeName = "TEXT")] + public string? MessageDetailJson { get; set; } + + /// + /// 小区ID + /// + [Column("CellID")] + public int? CellID { get; set; } + + /// + /// 国际移动用户识别码 + /// + [Column("IMSI", TypeName = "VARCHAR(20)")] + [MaxLength(20)] + public string? IMSI { get; set; } + + /// + /// 日志方向类型 + /// + [Required] + [Column("Direction")] + public int Direction { get; set; } + + /// + /// 用户设备ID + /// + [Column("UEID")] + public int? UEID { get; set; } + + /// + /// 公共陆地移动网络标识 + /// + [Column("PLMN", TypeName = "VARCHAR(10)")] + [MaxLength(10)] + public string? PLMN { get; set; } + + /// + /// 时间间隔(毫秒) + /// + [Column("TimeMs")] + public long TimeMs { get; set; } + + /// + /// 时间戳 + /// + [Required] + [Column("Timestamp")] + public long Timestamp { get; set; } + + /// + /// 信息字段 + /// + [Column("Info", TypeName = "TEXT")] + public string? Info { get; set; } + + /// + /// 消息字段 + /// + [Column("Message", TypeName = "TEXT")] + public string? Message { get; set; } + + + /// + /// 消息详情集合(非数据库字段,用于业务逻辑) + /// + [NotMapped] + public IEnumerable? MessageDetail + { + get => !string.IsNullOrEmpty(MessageDetailJson) + ? Newtonsoft.Json.JsonConvert.DeserializeObject>(MessageDetailJson) + : null; + set => MessageDetailJson = value != null + ? Newtonsoft.Json.JsonConvert.SerializeObject(value) + : null; + } + + /// + /// 时间间隔(用于业务逻辑) + /// + [NotMapped] + public TimeSpan Time + { + get => TimeSpan.FromMilliseconds(TimeMs); + set => TimeMs = (long)value.TotalMilliseconds; + } + } +} \ No newline at end of file diff --git a/CoreAgent.ProtocolClient/Models/ProtocolLogJson.cs b/CoreAgent.ProtocolClient/Models/ProtocolLogJson.cs new file mode 100644 index 0000000..7d6c0bc --- /dev/null +++ b/CoreAgent.ProtocolClient/Models/ProtocolLogJson.cs @@ -0,0 +1,168 @@ +using Newtonsoft.Json.Linq; +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CoreAgent.ProtocolClient.Models +{ + public record class ProtocolLogJson + { + /// + /// 消息ID + /// + [JsonProperty("message_id")] + public int? MessageId { get; set; } + + /// + /// 消息头信息 + /// + [JsonProperty("headers")] + public string[]? Headers { get; set; } + + /// + /// 消息内容 + /// + [JsonProperty("message")] + public string Message { get; set; } + + /// + /// 时间戳 + /// + [JsonProperty("time")] + public double? Time { get; set; } + + /// + /// 日志明细 + /// + [JsonProperty("logs")] + public JToken? Logs { get; set; } + + /// + /// 初始化协议日志实体类的新实例 + /// + /// 消息内容 + /// 消息类型 + /// 协议版本 + /// 时间戳 + /// UTC时间戳 + /// 日志明细 + /// 消息ID + /// 消息头信息 + public ProtocolLogJson( + string message, + string? type, + string? version, + double? time, + double? utc, + JToken? logs, + int? messageId, + string[]? headers) + { + Message = message; + Time = time; + Logs = logs; + MessageId = messageId ?? 0; + Headers = headers; + } + } + + + /// + /// 协议日志明细 + /// + public class ProtocolLogDetailJson + { + /// + /// 源信息 + /// + [JsonProperty("src")] + public string Src { get; set; } + + /// + /// 索引 + /// + [JsonProperty("idx")] + public int Idx { get; set; } + + /// + /// 日志级别 + /// + [JsonProperty("level")] + public int Level { get; set; } + + /// + /// 方向 + /// + [JsonProperty("dir")] + public string Dir { get; set; } + + /// + /// 时间戳 + /// + [JsonProperty("timestamp")] + public long Timestamp { get; set; } + + /// + /// 小区信息 + /// + [JsonProperty("cell")] + public int? Cell { get; set; } + + /// + /// 数据列表 + /// + [JsonProperty("data")] + public List Data { get; set; } + + /// + /// 层信息 + /// + [JsonProperty("layer")] + public string Layer { get; set; } + + /// + /// UE标识 + /// + [JsonProperty("ue_id")] + public int? UeId { get; set; } + + /// + /// 帧信息 + /// + [JsonProperty("frame")] + public int? Frame { get; set; } + + /// + /// 时隙信息 + /// + [JsonProperty("slot")] + public int? Slot { get; set; } + + /// + /// 帧信息 + /// + [JsonProperty("channel")] + public string? Channel { get; set; } + + /// + /// 时隙信息 + /// + [JsonProperty("rnti")] + public int? Rnti { get; set; } + + + /// + /// 深拷贝当前对象 + /// + public ProtocolLogDetailJson DeepClone() + { + // 通过序列化和反序列化实现深拷贝 + var json = JsonConvert.SerializeObject(this); + return JsonConvert.DeserializeObject(json) + ?? throw new InvalidOperationException("深拷贝失败,反序列化结果为 null"); + } + } +} diff --git a/CoreAgent.ProtocolClient/Models/TmsiMatchProcessor.cs b/CoreAgent.ProtocolClient/Models/TmsiMatchProcessor.cs new file mode 100644 index 0000000..65c2879 --- /dev/null +++ b/CoreAgent.ProtocolClient/Models/TmsiMatchProcessor.cs @@ -0,0 +1,324 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace CoreAgent.ProtocolClient.Models +{ + /// + /// TMSI匹配处理器 + /// 负责处理TMSI匹配和UE链的构建,从根节点获取IMSI并分配给整个链 + /// + public class TmsiMatchProcessor + { + /// + /// TMSI到UE ID的映射表 + /// + private readonly Dictionary _tmsiToUeId; + + /// + /// 请求TMSI到UE ID的映射表 + /// + private readonly Dictionary _requestTmsiToUeId; + + /// + /// IMSI到UE ID的映射表 + /// + private readonly Dictionary> _imsiToUeId; + + /// + /// 构造函数 + /// + /// TMSI到UE ID的映射表 + /// 请求TMSI到UE ID的映射表 + /// IMSI到UE ID的映射表 + public TmsiMatchProcessor( + Dictionary tmsiToUeId, + Dictionary requestTmsiToUeId, + Dictionary> imsiToUeId) + { + _tmsiToUeId = tmsiToUeId ?? new Dictionary(); + _requestTmsiToUeId = requestTmsiToUeId ?? new Dictionary(); + _imsiToUeId = imsiToUeId ?? new Dictionary>(); + } + + /// + /// 生成TMSI匹配结果 + /// 先构建树形结构,从根节点获取IMSI,然后平铺成列表 + /// + /// TMSI匹配结果列表 + public List GenerateTmsiMatches() + { + var matches = new List(); + + // 构建UE链的树形结构 + var ueChains = BuildUeChains(); + + // 遍历RequestTmsiToUeId,查找在TmsiToUeId中是否有相同的TMSI + foreach (var requestKvp in _requestTmsiToUeId) + { + uint tmsi = requestKvp.Key; + int requestUeId = requestKvp.Value; + + // 检查TmsiToUeId中是否存在相同的TMSI + if (_tmsiToUeId.TryGetValue(tmsi, out int receiveUeId)) + { + // 从UE链中获取IMSI + string imsi = GetImsiFromUeChain(receiveUeId, ueChains); + + // 创建匹配结果 + var matchResult = new TmsiMatchResult(tmsi, requestUeId, receiveUeId, imsi); + matches.Add(matchResult); + } + } + + return matches; + } + + /// + /// 构建UE链的树形结构 + /// 根据RequestTmsiToUeId和TmsiToUeId的映射关系构建链式结构 + /// + /// UE链的字典,键为链的根节点UE ID,值为链中的所有UE ID + private Dictionary> BuildUeChains() + { + var ueChains = new Dictionary>(); + var processedUeIds = new HashSet(); + + // 遍历所有TMSI匹配关系,构建链式结构 + foreach (var requestKvp in _tmsiToUeId) + { + uint tmsi = requestKvp.Key; + int requestUeId = requestKvp.Value; + + if (_requestTmsiToUeId.TryGetValue(tmsi, out int receiveUeId)) + { + // 如果这个UE ID还没有被处理过 + if (!processedUeIds.Contains(requestUeId) && !processedUeIds.Contains(receiveUeId)) + { + var chain = BuildChainFromUe(requestUeId, receiveUeId); + if (chain.Count > 0) + { + // 使用链的根节点(最外层节点)作为键 + int rootUeId = chain[0]; + ueChains[rootUeId] = chain; + + // 标记所有UE ID为已处理 + foreach (int ueId in chain) + { + processedUeIds.Add(ueId); + } + } + } + } + } + + return ueChains; + } + + /// + /// 从指定的UE ID开始构建链式结构 + /// + /// 请求UE ID + /// 接收UE ID + /// 链中的所有UE ID列表 + private List BuildChainFromUe(int requestUeId, int receiveUeId) + { + var chain = new List(); + var visited = new HashSet(); + + // 从requestUeId开始,沿着链式关系查找 + int currentUeId = requestUeId; + while (currentUeId > 0 && !visited.Contains(currentUeId)) + { + chain.Add(currentUeId); + visited.Add(currentUeId); + + // 查找下一个UE ID(在TmsiToUeId中查找当前UE ID作为receiveUeId的情况) + currentUeId = FindNextUeIdInChain(currentUeId); + } + + return chain; + } + + /// + /// 在链中查找下一个UE ID + /// + /// 当前UE ID + /// 下一个UE ID,如果没有找到则返回-1 + private int FindNextUeIdInChain(int currentUeId) + { + // 在RequestTmsiToUeId中查找当前UE ID作为receiveUeId的情况 + foreach (var kvp in _requestTmsiToUeId) + { + if (_tmsiToUeId.TryGetValue(kvp.Key, out int receiveUeId) && receiveUeId == currentUeId) + { + return kvp.Value; // 返回对应的requestUeId + } + } + + return -1; // 没有找到下一个UE ID + } + + /// + /// 从UE链中获取IMSI + /// 优先从链的根节点(最外层节点)获取IMSI + /// + /// 当前UE ID + /// UE链字典 + /// 对应的IMSI,如果未找到则返回空字符串 + private string GetImsiFromUeChain(int ueId, Dictionary> ueChains) + { + // 查找当前UE ID所在的链 + foreach (var chain in ueChains.Values) + { + if (chain.Contains(ueId)) + { + // 从链的根节点(最外层节点)开始查找IMSI + foreach (int chainUeId in chain) + { + string imsi = GetImsiForUeId(chainUeId); + if (!string.IsNullOrEmpty(imsi)) + { + return imsi; + } + } + break; + } + } + + return string.Empty; + } + + /// + /// 根据UE ID获取对应的IMSI + /// 优先查找当前UE ID,如果未找到则查找整个UE链中的IMSI + /// + /// UE ID + /// 对应的IMSI,如果未找到则返回空字符串 + private string GetImsiForUeId(int ueId) + { + // 1. 首先查找当前UE ID对应的IMSI + foreach (var imsiMapping in _imsiToUeId) + { + if (imsiMapping.Value.Contains(ueId)) + { + return imsiMapping.Key; + } + } + + // 2. 如果当前UE ID没有IMSI,查找整个UE链中的IMSI + // 遍历所有IMSI映射,查找是否有任何UE ID对应的IMSI + foreach (var imsiMapping in _imsiToUeId) + { + if (!string.IsNullOrEmpty(imsiMapping.Key)) + { + return imsiMapping.Key; + } + } + + return string.Empty; + } + + /// + /// 获取UE链的统计信息 + /// + /// UE链统计信息 + public UeChainStats GetUeChainStats() + { + var ueChains = BuildUeChains(); + + return new UeChainStats + { + TotalChains = ueChains.Count, + TotalUeIds = ueChains.Values.Sum(chain => chain.Count), + AverageChainLength = ueChains.Count > 0 ? (double)ueChains.Values.Sum(chain => chain.Count) / ueChains.Count : 0, + MaxChainLength = ueChains.Count > 0 ? ueChains.Values.Max(chain => chain.Count) : 0, + MinChainLength = ueChains.Count > 0 ? ueChains.Values.Min(chain => chain.Count) : 0 + }; + } + + /// + /// 获取所有UE链的详细信息 + /// + /// UE链详细信息列表 + public List GetUeChainDetails() + { + var ueChains = BuildUeChains(); + var chainInfos = new List(); + + foreach (var kvp in ueChains) + { + int rootUeId = kvp.Key; + var chain = kvp.Value; + string imsi = GetImsiFromUeChain(rootUeId, ueChains); + + chainInfos.Add(new UeChainInfo + { + RootUeId = rootUeId, + ChainLength = chain.Count, + UeIds = chain.ToList(), + Imsi = imsi + }); + } + + return chainInfos; + } + } + + /// + /// UE链统计信息 + /// + public class UeChainStats + { + /// + /// 总链数 + /// + public int TotalChains { get; set; } + + /// + /// 总UE ID数 + /// + public int TotalUeIds { get; set; } + + /// + /// 平均链长度 + /// + public double AverageChainLength { get; set; } + + /// + /// 最大链长度 + /// + public int MaxChainLength { get; set; } + + /// + /// 最小链长度 + /// + public int MinChainLength { get; set; } + } + + /// + /// UE链详细信息 + /// + public class UeChainInfo + { + /// + /// 根节点UE ID + /// + public int RootUeId { get; set; } + + /// + /// 链长度 + /// + public int ChainLength { get; set; } + + /// + /// 链中的所有UE ID + /// + public List UeIds { get; set; } = new List(); + + /// + /// 对应的IMSI + /// + public string Imsi { get; set; } = string.Empty; + } +} \ No newline at end of file diff --git a/CoreAgent.ProtocolClient/Models/TmsiMatchResult.cs b/CoreAgent.ProtocolClient/Models/TmsiMatchResult.cs new file mode 100644 index 0000000..11d1ce6 --- /dev/null +++ b/CoreAgent.ProtocolClient/Models/TmsiMatchResult.cs @@ -0,0 +1,56 @@ +using System; + +namespace CoreAgent.ProtocolClient.Models +{ + /// + /// TMSI匹配结果模型 + /// 用于存储通过TMSI匹配的请求UE ID和接收UE ID + /// + public class TmsiMatchResult + { + /// + /// TMSI标识符 + /// + public uint Tmsi { get; set; } + + /// + /// 请求UE ID + /// + public int RequestUeId { get; set; } + + /// + /// 接收UE ID + /// + public int ReceiveUeId { get; set; } + + /// + /// IMSI标识符 + /// + public string Imsi { get; set; } = string.Empty; + + /// + /// 构造函数 + /// + /// TMSI标识符 + /// 请求UE ID + /// 接收UE ID + /// IMSI标识符 + public TmsiMatchResult(uint tmsi, int requestUeId, int receiveUeId, string imsi = "") + { + Tmsi = tmsi; + RequestUeId = requestUeId; + ReceiveUeId = receiveUeId; + Imsi = imsi; + } + + /// + /// 重写ToString方法,提供友好的字符串表示 + /// + /// 格式化的字符串 + public override string ToString() + { + var imsiInfo = string.IsNullOrEmpty(Imsi) ? "" : $", IMSI: {Imsi}"; + return $"TMSI: 0x{Tmsi:X8}, RequestUE: {RequestUeId}, ReceiveUE: {ReceiveUeId}{imsiInfo}"; + } + } +} \ No newline at end of file diff --git a/CoreAgent.ProtocolClient/Models/UeInfo.cs b/CoreAgent.ProtocolClient/Models/UeInfo.cs new file mode 100644 index 0000000..f6e6a41 --- /dev/null +++ b/CoreAgent.ProtocolClient/Models/UeInfo.cs @@ -0,0 +1,62 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CoreAgent.ProtocolClient.Models +{ + /// + /// 用户设备(UE)信息模型 + /// 用于存储用户设备的基本信息,包括标识符、设备能力等 + /// + public class UeInfo + { + /// + /// 用户设备唯一标识符 + /// + public int UeId { get; set; } + + /// + /// 国际移动用户识别码(IMSI) + /// 用于唯一标识移动网络中的用户 + /// + public string? Imsi { get; set; } + + /// + /// 国际移动设备识别码(IMEI) + /// 用于唯一标识移动设备硬件 + /// + public string? Imei { get; set; } + + /// + /// 协议能力信息 + /// 包含UE支持的协议栈能力和配置 + /// + public ProtocolCaps? Caps { get; set; } + } + + /// + /// 帧信息模型 + /// 用于存储无线帧相关的时序信息 + /// + public class FrameInfo + { + /// + /// 最后处理的帧号 + /// + public int Last { get; set; } + + /// + /// 超帧号(Hyper Frame Number) + /// 用于LTE系统中的帧同步 + /// + public int Hfn { get; set; } + + /// + /// 时间戳(毫秒) + /// 默认为-1表示未初始化 + /// + public long Timestamp { get; set; } = -1; + } +} diff --git a/CoreAgent.ProtocolClient/modify.md b/CoreAgent.ProtocolClient/modify.md new file mode 100644 index 0000000..f87c59a --- /dev/null +++ b/CoreAgent.ProtocolClient/modify.md @@ -0,0 +1,153 @@ +# Models文件夹修改记录 + +## 修改日期 +2024年12月19日 + +## 修改概述 +对Models文件夹中的所有文件进行了命名规范检查和注释改进,主要涉及以下几个方面: + +### 1. 命名规范修复 + +#### ProtocolCaps.cs +- **修复方法命名**:将 `getCategory` 改为 `GetCategory`(PascalCase) +- **修复变量命名**:将 `Cat` 改为 `categories`,`Caps` 改为 `caps` +- **修复字符串拼接**:修正了类别信息的格式化逻辑 +- **修复扩展方法类命名**:将 `ProtocolCapsExtend` 改为 `ProtocolCapsExtensions` + +#### UEInfo.cs +- **修复类命名**:将 `UEInfo` 改为 `UeInfo`(PascalCase) +- **修复文件名**:将 `UEInfo.cs` 重命名为 `UeInfo.cs` +- **保持一致性**:确保所有类名都遵循C#命名约定 + +#### CellConfig.cs +- **保持JSON序列化兼容性**:保留原有的下划线命名法用于JSON序列化 +- **添加JsonProperty特性**:为所有属性添加了 `[JsonProperty]` 特性,确保与外部API的命名约定保持一致 +- **C#属性名使用PascalCase**:在C#代码中使用标准的PascalCase命名 +- **双重命名支持**: + - C#属性名:`NAntennaDl`、`NLayerDl` 等(PascalCase) + - JSON属性名:`n_antenna_dl`、`n_layer_dl` 等(下划线命名法) + +#### ProtocolLog.cs +- **修复扩展方法类命名**:将 `ProtocolLogExtend` 改为 `ProtocolLogExtensions` +- **添加详细注释**:为扩展方法类和方法添加了完整的注释 + +### 2. 类名规范检查 + +#### 文件名与类名对应关系 +所有文件的命名都符合C#规范: + +| 文件名 | 主要类名 | 其他类名 | 状态 | +|--------|----------|----------|------| +| `CellConfig.cs` | `CellConfig` | `PucchAllocation`, `SrsResources`, `GbrConfig`, `PlmnItem` | ✅ | +| `ClientConfig.cs` | `ClientConfig` | `ClientLogsConfig`, `LogLayerConfig` | ✅ | +| `LogLayerHelp.cs` | `LogLayerHelp` | 无 | ✅ | +| `MessageHandler.cs` | `MessageHandler` | 无 | ✅ | +| `ProtocolCaps.cs` | `ProtocolCaps` | `ProtocolCapsExtensions` | ✅ | +| `ProtocolLog.cs` | `ProtocolLog` | `PhyFields`, `DataFields`, `MacFields`, `LinkIds`, `ProtocolLogExtensions` | ✅ | +| `ProtocolLogDetail.cs` | `ProtocolLogDetail` | 无 | ✅ | +| `ProtocolLogJson.cs` | `ProtocolLogJson` | `ProtocolLogDetailJson` | ✅ | +| `TmsiMatchProcessor.cs` | `TmsiMatchProcessor` | `UeChainStats`, `UeChainInfo` | ✅ | +| `TmsiMatchResult.cs` | `TmsiMatchResult` | 无 | ✅ | +| `UeInfo.cs` | `UeInfo` | `FrameInfo` | ✅ | + +### 3. 注释改进 + +#### ProtocolCaps.cs +- 添加了详细的类注释,说明LTE协议能力信息的用途 +- 为所有属性添加了详细的中文注释 +- 为扩展方法类添加了说明注释 +- 改进了方法注释,包含参数和返回值说明 + +#### UEInfo.cs +- 添加了详细的类注释,说明用户设备信息模型的用途 +- 为所有属性添加了详细的中文注释,包括IMSI、IMEI等技术术语说明 +- 为FrameInfo类添加了详细的注释 + +#### CellConfig.cs +- 添加了详细的类注释,说明小区配置的用途和JSON序列化支持 +- 为所有属性添加了详细的中文注释,包括技术参数说明 +- 为嵌套类添加了详细的注释说明 +- 明确说明了JSON序列化的命名约定 + +#### MessageHandler.cs +- 改进了类注释,详细说明了消息处理器的功能 +- 为所有属性添加了更详细的注释说明 + +#### LogLayerHelp.cs +- 添加了详细的类注释,说明日志层配置帮助类的用途 +- 为方法添加了详细的注释,说明返回值的含义 + +#### ProtocolLog.cs +- 为扩展方法类添加了详细的注释 +- 为扩展方法添加了参数和返回值说明 + +### 4. 代码质量改进 + +#### ProtocolCaps.cs +- 修复了字符串拼接逻辑错误 +- 改进了变量命名,提高代码可读性 +- 优化了条件判断逻辑 +- 修复了扩展方法类命名 + +#### CellConfig.cs +- 添加了Newtonsoft.Json引用 +- 实现了双重命名支持,既符合C#命名约定,又保持JSON序列化兼容性 +- 改进了注释的可读性和准确性 + +#### ProtocolLog.cs +- 修复了扩展方法类命名,符合C#约定 +- 添加了详细的注释说明 + +## 修改文件列表 + +1. **ProtocolCaps.cs** - 修复方法命名、扩展方法类命名和注释 +2. **UEInfo.cs** - 修复类命名、文件名和注释 +3. **CellConfig.cs** - 添加JSON序列化特性,保持API兼容性 +4. **MessageHandler.cs** - 改进注释 +5. **LogLayerHelp.cs** - 改进注释 +6. **ProtocolLog.cs** - 修复扩展方法类命名和注释 + +## 影响范围 + +- 所有修改都遵循C#命名约定 +- 注释改进提高了代码的可维护性 +- CellConfig保持了与外部API的完全兼容性 +- 扩展方法类命名符合C#约定 +- 没有破坏现有的JSON序列化功能 + +## 重要说明 + +### CellConfig的JSON序列化设计 +- **设计原则**:保持与外部API的命名约定一致性 +- **实现方式**:使用 `[JsonProperty]` 特性指定JSON属性名 +- **优势**: + - C#代码使用标准PascalCase命名,符合C#约定 + - JSON序列化使用下划线命名法,与外部API保持一致 + - 无需修改现有API调用代码 + - 支持双向序列化和反序列化 + +### 扩展方法类命名规范 +- **C#约定**:扩展方法类应以 `Extensions` 结尾 +- **修改内容**: + - `ProtocolCapsExtend` → `ProtocolCapsExtensions` + - `ProtocolLogExtend` → `ProtocolLogExtensions` + +### 示例 +```csharp +// C#属性名(PascalCase) +public int NAntennaDl { get; set; } + +// JSON序列化后的名称(下划线命名法) +[JsonProperty("n_antenna_dl")] + +// 扩展方法类命名(符合C#约定) +public static class ProtocolCapsExtensions +``` + +## 注意事项 + +- CellConfig的修改确保了向后兼容性,不会影响现有的API调用 +- 扩展方法类重命名可能需要更新相关的引用代码 +- 其他文件的命名修改可能需要更新相关的引用代码 +- 建议在部署前进行完整的测试,确保所有功能正常工作 +- 特别注意JSON序列化/反序列化功能的测试 \ No newline at end of file diff --git a/CoreAgent.sln b/CoreAgent.sln index aa995c0..5181c4c 100644 --- a/CoreAgent.sln +++ b/CoreAgent.sln @@ -11,6 +11,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CoreAgent.Domain", "CoreAge EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CoreAgent.Infrastructure", "CoreAgent.Infrastructure\CoreAgent.Infrastructure.csproj", "{3EBCA8A2-F1CD-23F7-C943-0CAADACFFCDD}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CoreAgent.ProtocolClient", "CoreAgent.ProtocolClient\CoreAgent.ProtocolClient.csproj", "{9A98C9B3-3B8F-4D85-A63A-7DEC02423877}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -33,8 +35,15 @@ Global {3EBCA8A2-F1CD-23F7-C943-0CAADACFFCDD}.Debug|Any CPU.Build.0 = Debug|Any CPU {3EBCA8A2-F1CD-23F7-C943-0CAADACFFCDD}.Release|Any CPU.ActiveCfg = Release|Any CPU {3EBCA8A2-F1CD-23F7-C943-0CAADACFFCDD}.Release|Any CPU.Build.0 = Release|Any CPU + {9A98C9B3-3B8F-4D85-A63A-7DEC02423877}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9A98C9B3-3B8F-4D85-A63A-7DEC02423877}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9A98C9B3-3B8F-4D85-A63A-7DEC02423877}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9A98C9B3-3B8F-4D85-A63A-7DEC02423877}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {C0E4B0F3-63EC-4BF6-BA62-335DAC0686B7} + EndGlobalSection EndGlobal