Browse Source

优化ILogger使用和组件依赖关系 - 为UeIdentifierManager和ProtocolLogContext添加ILogger支持 - 将ILogger参数改为必填,提高代码健壮性 - 为ProtocolClientContext添加ILoggerFactory支持 - 优化ProtocolLogContext的组件间依赖,通过上下文访问兄弟组件 - 简化方法签名,移除不必要的参数传递 - 统一日志管理,遵循Microsoft.Extensions.Logging最佳实践

feature/protocol-log-Perfect
root 5 months ago
parent
commit
3550787281
  1. 118
      CoreAgent.ProtocolClient/Context/CellParameterManager.cs
  2. 20
      CoreAgent.ProtocolClient/Context/ProtocolBasicInfo.cs
  3. 40
      CoreAgent.ProtocolClient/Context/ProtocolClientContext.cs
  4. 22
      CoreAgent.ProtocolClient/Context/ProtocolFeatureFlags.cs
  5. 80
      CoreAgent.ProtocolClient/Context/ProtocolLogContext.cs
  6. 71
      CoreAgent.ProtocolClient/Context/ProtocolLogPatterns.cs
  7. 154
      CoreAgent.ProtocolClient/Context/UeStateManager/ProtocolParsing.cs
  8. 138
      CoreAgent.ProtocolClient/Context/UeStateManager/StringIdManagement.cs
  9. 261
      CoreAgent.ProtocolClient/Context/UeStateManager/TmsiMatching.cs
  10. 126
      CoreAgent.ProtocolClient/Context/UeStateManager/UeIdentifierException.cs
  11. 205
      CoreAgent.ProtocolClient/Context/UeStateManager/UeIdentifierManager.cs
  12. 271
      CoreAgent.ProtocolClient/Context/UeStateManager/UeIdentifierMapping.cs
  13. 244
      CoreAgent.ProtocolClient/Context/UeStateManager/UeInfoManagement.cs
  14. 4
      CoreAgent.ProtocolClient/CoreAgent.ProtocolClient.csproj
  15. 509
      CoreAgent.ProtocolClient/Docs/MessageIdManager.md
  16. 29
      CoreAgent.ProtocolClient/Enums/TmsiType.cs
  17. 61
      CoreAgent.ProtocolClient/Managers/MessageIdManager.cs
  18. 40
      CoreAgent.ProtocolClient/Managers/WebSocketMgr/Constructor.cs
  19. 51
      CoreAgent.ProtocolClient/Managers/WebSocketMgr/Dispose.cs
  20. 203
      CoreAgent.ProtocolClient/Managers/WebSocketMgr/PrivateMethods.cs
  21. 283
      CoreAgent.ProtocolClient/Managers/WebSocketMgr/PublicMethods.cs
  22. 408
      CoreAgent.ProtocolClient/Managers/WebSocketMgr/WebSocketMessageManager.cs
  23. 25
      CoreAgent.ProtocolClient/Models/BuildProtocolLog.cs
  24. 26
      CoreAgent.ProtocolClient/Models/SourceProtocolLog.cs
  25. 14
      CoreAgent.ProtocolClient/Models/TmsiMatchProcessor.cs
  26. 63
      CoreAgent.ProtocolClient/Models/TmsiMatchResult.cs
  27. 6
      CoreAgent.ProtocolClient/Models/TransferProtocolLog.cs
  28. 2
      CoreAgent.ProtocolClient/Models/UeInfo.cs
  29. 1127
      CoreAgent.ProtocolClient/modify.md

118
CoreAgent.ProtocolClient/Context/CellParameterManager.cs

@ -0,0 +1,118 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using CoreAgent.ProtocolClient.Models;
using Newtonsoft.Json;
namespace CoreAgent.ProtocolClient.Context
{
/// <summary>
/// 参数管理
/// </summary>
public class CellParameterManager
{
public Dictionary<int, CellConfig> Parameters { get; set; } = new();
public List<string> RanIds { get; set; } = new();
public void SetRanId(string ranId)
{
RanIds.Add(ranId);
}
public void AddCell(int id, CellConfig config)
{
if (Parameters.ContainsKey(id))
{
Parameters[id] = config;
}
else
{
Parameters.Add(id, config);
}
}
public void SetHeaders(string[] headers, ProtocolBasicInfo basicInfo)
{
basicInfo.Headers = headers;
var cells = new List<Dictionary<string, object>>();
for (int i = 0; i < headers.Length; i++)
{
var header = headers[i];
Match? info;
info = Regex.Match(header, @"lte(\w+) version ([\d-]+)", RegexOptions.IgnoreCase);
if (info.Success)
{
basicInfo.Model = info.Groups[1].Value.ToUpper();
basicInfo.Version = info.Groups[2].Value;
}
else if (Regex.IsMatch(header, @"Licensed to"))
{
basicInfo.License = header;
}
else if ((info = Regex.Match(header, @"Metadata:(.+)$")).Success)
{
var metadata = JsonConvert.DeserializeObject<Dictionary<string, object>>(info.Groups[1].Value);
}
else if ((info = Regex.Match(header, @"(global_(ran_node|enb)_id)=([\d\.]+)")).Success)
{
this.SetRanId(info.Groups[3].Value);
}
else if ((info = Regex.Match(header, @"Cell 0x(\d+): (.*)")).Success)
{
var cell = new Dictionary<string, object>
{
["cell_id"] = int.Parse(info.Groups[1].Value, System.Globalization.NumberStyles.HexNumber),
["sib1Decoded"] = true,
["sib2Decoded"] = true
};
var list = info.Groups[2].Value.Split(' ');
foreach (var param in list)
{
var parts = param.Split('=');
if (parts.Length == 2)
{
cell[parts[0]] = HeaderCellParam(parts[0], parts[1]);
}
}
cells.Add(cell);
}
else if (cells.Count > 0)
{
info = Regex.Match(header, @"([UD]L): (.*)");
if (info.Success)
{
var cell = cells[cells.Count - 1];
var dir = info.Groups[1].Value;
var list = info.Groups[2].Value.Split(' ');
foreach (var param in list)
{
var parts = param.Split('=');
if (parts.Length == 2)
{
cell[parts[0]] = HeaderCellParam(parts[0], parts[1]);
}
}
}
}
}
}
private object HeaderCellParam(string param, string value)
{
switch (param)
{
case "br_dl_sf_bitmap":
case "nb_dl_sf_bitmap":
case "label":
return value;
default:
if (int.TryParse(value, out var intValue))
return intValue;
return value;
}
}
}
}

20
CoreAgent.ProtocolClient/Context/ProtocolBasicInfo.cs

@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CoreAgent.ProtocolClient.Context
{
/// <summary>
/// 基础信息
/// </summary>
public class ProtocolBasicInfo
{
public string? Name { get; set; }
public string? Version { get; set; }
public string? License { get; set; }
public string? Model { get; set; }
public string[]? Headers { get; set; }
}
}

40
CoreAgent.ProtocolClient/Context/ProtocolClientContext.cs

@ -0,0 +1,40 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using CoreAgent.ProtocolClient.Context.UeStateManager;
using CoreAgent.ProtocolClient.Enums;
using Microsoft.Extensions.Logging;
namespace CoreAgent.ProtocolClient.Context
{
public class ProtocolClientContext
{
// 常量和正则表达式已迁移至 ProtocolLogPatterns 静态类,便于统一管理和调用。
private readonly ILoggerFactory _loggerFactory;
public ProtocolBasicInfo BasicInfo { get; set; } = new();
public ProtocolFeatureFlags FeatureFlags { get; set; } = new();
public ProtocolLogContext LogContext { get; set; }
public UeIdentifierManager UeIdentifier { get; set; }
public CellParameterManager CellParameterManager { get; set; } = new();
public ClientState State { get; set; } = ClientState.Stop;
public ProtocolClientContext(ILoggerFactory loggerFactory)
{
_loggerFactory = loggerFactory ?? throw new ArgumentNullException(nameof(loggerFactory));
// 使用ILoggerFactory为各个组件创建正确的Logger
var ueIdentifierLogger = _loggerFactory.CreateLogger<UeIdentifierManager>();
var logContextLogger = _loggerFactory.CreateLogger<ProtocolLogContext>();
// 先创建UeIdentifierManager
UeIdentifier = new UeIdentifierManager(ueIdentifierLogger);
// 然后创建ProtocolLogContext,传入this引用
LogContext = new ProtocolLogContext(logContextLogger, this);
}
}
}

22
CoreAgent.ProtocolClient/Context/ProtocolFeatureFlags.cs

@ -0,0 +1,22 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CoreAgent.ProtocolClient.Context
{
/// <summary>
/// 功能标志
/// </summary>
public class ProtocolFeatureFlags
{
public bool HasCell { get; set; }
public bool HasPhy { get; set; }
public bool HasData { get; set; }
public bool HasRnti { get; set; }
public bool HasRb { get; set; }
public bool HasSignalRecord { get; set; }
public bool IsFirstGetLogs { get; set; } = true;
}
}

80
CoreAgent.ProtocolClient/Context/ProtocolLogContext.cs

@ -0,0 +1,80 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using CoreAgent.ProtocolClient.Context.UeStateManager;
using CoreAgent.ProtocolClient.Models;
using Microsoft.Extensions.Logging;
namespace CoreAgent.ProtocolClient.Context
{
/// <summary>
/// 日志相关
/// </summary>
public class ProtocolLogContext
{
private static int _logIdCounter;
private readonly ILogger<ProtocolLogContext> _logger;
private readonly ProtocolClientContext _context;
public List<SourceProtocolLog> Logs { get; set; } = new();
public int LogCount => Logs.Count;
public ProtocolLogContext(ILogger<ProtocolLogContext> logger, ProtocolClientContext context)
{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_context = context ?? throw new ArgumentNullException(nameof(context));
}
public void ResetLogs()
{
if (LogCount > 0)
{
Logs.Clear();
_context.FeatureFlags.HasCell = false;
_context.FeatureFlags.HasPhy = false;
_context.FeatureFlags.HasData = false;
_context.FeatureFlags.HasRnti = false;
_context.FeatureFlags.HasRb = false;
_context.FeatureFlags.HasSignalRecord = false;
}
}
/// <summary>
/// 更新日志的时间戳并分配唯一ID。
/// 检查并处理时间戳回绕,详细记录回绕前后各关键字段,包括:
/// 1. 原始日志时间戳(log.Timestamp)
/// 2. 当前偏移量(parser.TimestampOffset)
/// 3. 计算得到的当前时间戳(timestamp)
/// 4. 上次处理的时间戳(parser.LastTimestamp)
/// 5. 回绕后新的时间戳(timestamp + 86400000)
/// 6. 本次回绕的增量(86400000)
/// 7. 日志分配的唯一ID(log.Id)
/// </summary>
public void UpdateLogTimestampAndAssignId(ref BuildProtocolLog log)
{
// 检查时间戳回绕
var parser = _context.UeIdentifier;
var timestamp = log.Timestamp + parser.TimestampOffset;
if (timestamp < parser.LastTimestamp - 100)
{
_logger.LogInformation($"[Log回绕] 原始时间戳: {log.Timestamp}, 偏移量: {parser.TimestampOffset}, 回绕前: {timestamp}, parser.LastTimestamp(回绕前): {parser.LastTimestamp}, 回绕后: {timestamp + 86400000}, parser.LastTimestamp(回绕后): {parser.LastTimestamp}, 增量: 86400000, 日志ID(回绕前): {log.Id}");
timestamp += 86400000; // 24小时
parser.TimestampOffset += 86400000;
_logger.LogInformation($"[Log回绕后] 新时间戳: {timestamp}, parser.LastTimestamp(回绕后): {parser.LastTimestamp}, 新偏移量: {parser.TimestampOffset}, 日志ID(回绕后): {log.Id}");
}
var lastTimestampBefore = parser.LastTimestamp;
parser.LastTimestamp = log.Timestamp = timestamp;
log.Id = GenerateLogId();
_logger.LogDebug($"[Log分配ID] 日志最终时间戳: {log.Timestamp}, 分配ID: {log.Id}, 当前偏移量: {parser.TimestampOffset}, parser.LastTimestamp(分配前): {lastTimestampBefore}, parser.LastTimestamp(分配后): {parser.LastTimestamp}");
}
private static int GenerateLogId()
{
return Interlocked.Increment(ref _logIdCounter);
}
}
}

71
CoreAgent.ProtocolClient/Context/ProtocolLogPatterns.cs

@ -0,0 +1,71 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
namespace CoreAgent.ProtocolClient.Context
{
/// <summary>
/// 协议日志相关常量与正则表达式,便于全局统一管理和调用。
/// </summary>
public static class ProtocolLogPatterns
{
#region 常量定义
/// <summary>
/// HFN回绕阈值
/// </summary>
public const int HFN_WRAP_THRESHOLD = 512;
/// <summary>
/// 最大日志数量
/// </summary>
public const int LOGS_MAX = 2000000;
/// <summary>
/// BSR表 - 对应JavaScript中的_bsr_table
/// </summary>
public static readonly int[] BsrTable = {
0, 10, 12, 14, 17, 19, 22, 26, 31, 36, 42, 49, 57, 67, 78, 91, 107, 125, 146, 171, 200, 234, 274, 321, 376, 440, 515, 603, 706, 826, 967, 1132, 1326, 1552, 1817, 2127, 2490, 2915, 3413, 3995, 4677, 5476, 6411, 7505, 8787, 10287, 12043, 14099, 16507, 19325, 22624, 26487, 31009, 36304, 42502, 49759, 58255, 68201, 79846, 93479, 109439, 128125, 150000, 500000
};
#endregion
#region 正则表达式
public static readonly Regex RegExpPhy = new(@"^([a-f0-9\-]+)\s+([a-f0-9\-]+)\s+([\d\.\-]+) (\w+): (.+)", RegexOptions.Compiled);
public static readonly Regex RegExpInfo1 = new(@"^([\w\-]+): (.+)", RegexOptions.Compiled);
public static readonly Regex RegExpInfo2 = new(@"^([\w]+) (.+)", RegexOptions.Compiled);
public static readonly Regex RegExpIP = new(@"^(len=\d+)\s+(\S+)\s+(.*)", RegexOptions.Compiled);
public static readonly Regex RegExpIPsec = new(@"^len=(\d+)\s+(.*)", RegexOptions.Compiled);
public static readonly Regex RegExpSDULen = new(@"SDU_len=(\d+)", RegexOptions.Compiled);
public static readonly Regex RegExpSIP = new(@"^([:\.\[\]\da-f]+)\s+(\S+) (.+)", RegexOptions.Compiled);
public static readonly Regex RegExpMediaReq = new(@"^(\S+) (.+)", RegexOptions.Compiled);
public static readonly Regex RegExpSignalRecord = new(@"Link:\s([\w\d]+)@(\d+)", RegexOptions.Compiled);
public static readonly Regex RegExpCellID = new(@"^([a-f0-9\-]+) (.+)", RegexOptions.Compiled);
public static readonly Regex RegExpRRC_UE_ID = new(@"Changing UE_ID to 0x(\d+)", RegexOptions.Compiled);
public static readonly Regex RegExpRRC_TMSI = new(@"(5G|m)-TMSI '([\dA-F]+)'H", RegexOptions.Compiled);
public static readonly Regex RegExpRRC_NEW_ID = new(@"newUE-Identity (['\dA-FH]+)", RegexOptions.Compiled);
public static readonly Regex RegExpRRC_CRNTI = new(@"c-RNTI '([\dA-F]+)'H", RegexOptions.Compiled);
public static readonly Regex RegExpNAS_TMSI = new(@"[Mm]-TMSI = 0x([\da-f]+)", RegexOptions.Compiled);
public static readonly Regex RegExpNAS_5GTMSI = new(@"5G-TMSI = 0x([\da-f]+)", RegexOptions.Compiled);
public static readonly Regex RegExpRRC_BC = new(@"(EUTRA|MRDC|NR|NRDC) band combinations", RegexOptions.Compiled);
public static readonly Regex RegExpPDCCH = new(@"^\s*(.+)=(\d+)$", RegexOptions.Compiled);
public static readonly Regex RegExpS1NGAP = new(@"^([\da-f\-]+)\s+([\da-f\-]+) (([^\s]+) .+)", RegexOptions.Compiled);
public static readonly Regex RegExpHexDump = new(@"^[\da-f]+:(\s+[\da-f]{2}){1,16}\s+.{1,16}$", RegexOptions.Compiled);
public static readonly Regex RegMccPattern = new Regex(@"MCC\s*=\s*(\d+)", RegexOptions.Compiled);
public static readonly Regex RegMncPattern = new Regex(@"MNC\s*=\s*(\d+)", RegexOptions.Compiled);
public static readonly Regex RegMSINPattern = new Regex(@"MSIN\s*=\s*(\d+)", RegexOptions.Compiled);
public static readonly Regex RegIMSIPattern = new Regex(@"IMSI = \s*(\d+)", RegexOptions.Compiled);
public static Match? FindData(this Regex regex, List<string> data)
{
return data?.FirstOrDefault(line => regex.IsMatch(line)) is string matchedLine
? regex.Match(matchedLine)
: null;
}
#endregion
}
}

154
CoreAgent.ProtocolClient/Context/UeStateManager/ProtocolParsing.cs

@ -0,0 +1,154 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using CoreAgent.ProtocolClient.Enums;
using CoreAgent.ProtocolClient.Models;
using Microsoft.Extensions.Logging;
namespace CoreAgent.ProtocolClient.Context.UeStateManager
{
/// <summary>
/// UeIdentifierManager 协议解析部分
/// </summary>
public partial class UeIdentifierManager
{
#region 协议解析方法
/// <summary>
/// 解析提示信息
/// </summary>
/// <param name="log">协议日志</param>
/// <param name="config">解析配置</param>
public void ParseHints(BuildProtocolLog log, Dictionary<string, object> config)
{
if (log?.Info != (int)LogChannelId.ID_HINTS)
{
_logger?.LogDebug("跳过非提示信息日志: Info={Info}", log?.Info);
return;
}
try
{
var hints = ParseArguments(log.Message, config);
_logger?.LogDebug("解析提示信息成功,共解析 {Count} 个参数", hints.Count);
// TODO: 处理解析后的提示信息
}
catch (Exception ex)
{
_logger?.LogError(ex, "解析提示信息失败: {Message}", log.Message);
throw new ParsingException("Hints", log?.Message ?? "", $"解析提示信息失败: {ex.Message}", ex);
}
}
/// <summary>
/// 解析参数字符串为字典
/// 性能优化:使用编译后的正则表达式
/// </summary>
/// <param name="arguments">参数字符串,格式如 "a=1 b=2 c=hello"</param>
/// <param name="config">
/// 配置字典,key为参数名,value为类型(Type)或处理函数(Func&lt;string, object&gt;)
/// 例如:{ "a", typeof(int) }, { "b", (Func&lt;string, object&gt;)(s =&gt; s.ToUpper()) }
/// </param>
/// <returns>解析后的参数字典</returns>
private Dictionary<string, object> ParseArguments(string arguments, Dictionary<string, object> config)
{
if (string.IsNullOrEmpty(arguments))
{
return new Dictionary<string, object>();
}
var result = new Dictionary<string, object>();
// 使用编译后的正则表达式提高性能
var whitespaceRegex = new Regex(@"\s+", RegexOptions.Compiled);
try
{
foreach (var argument in whitespaceRegex.Split(arguments))
{
if (string.IsNullOrWhiteSpace(argument))
continue;
var keyValuePair = argument.Split('=', 2);
if (keyValuePair.Length != 2)
{
_logger?.LogWarning("跳过格式无效的参数: {Argument}", argument);
continue;
}
var key = keyValuePair[0];
var value = keyValuePair[1];
result[key] = ParseValue(value, key, config);
}
return result;
}
catch (Exception ex)
{
_logger?.LogError(ex, "解析参数字符串失败: {Arguments}", arguments);
throw new ParsingException("Arguments", arguments, $"解析参数字符串失败: {ex.Message}", ex);
}
}
/// <summary>
/// 根据配置解析参数值
/// </summary>
/// <param name="value">原始值字符串</param>
/// <param name="key">参数键</param>
/// <param name="config">解析配置</param>
/// <returns>解析后的值</returns>
private object ParseValue(string value, string key, Dictionary<string, object> config)
{
if (!config.TryGetValue(key, out var typeOrFunc))
return value;
try
{
return typeOrFunc switch
{
Type type when type == typeof(int) => int.TryParse(value, out var intValue) ? intValue : 0,
Type type when type == typeof(double) => double.TryParse(value, out var doubleValue) ? doubleValue : 0.0,
Func<string, object> func => func(value),
_ => value
};
}
catch (Exception ex)
{
_logger?.LogError(ex, "解析参数值失败: Key={Key}, Value={Value}", key, value);
return value; // 返回原始值作为回退
}
}
/// <summary>
/// 批量解析参数
/// 性能优化:批量处理减少重复计算
/// </summary>
/// <param name="argumentsList">参数字符串列表</param>
/// <param name="config">解析配置</param>
/// <returns>解析结果列表</returns>
public List<Dictionary<string, object>> ParseArgumentsBatch(
IEnumerable<string> argumentsList,
Dictionary<string, object> config)
{
if (argumentsList == null)
{
throw new ArgumentNullException(nameof(argumentsList));
}
var results = new List<Dictionary<string, object>>();
foreach (var arguments in argumentsList)
{
results.Add(ParseArguments(arguments, config));
}
_logger?.LogDebug("批量解析参数完成,共处理 {Count} 个参数字符串", results.Count);
return results;
}
#endregion
}
}

138
CoreAgent.ProtocolClient/Context/UeStateManager/StringIdManagement.cs

@ -0,0 +1,138 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
namespace CoreAgent.ProtocolClient.Context.UeStateManager
{
/// <summary>
/// UeIdentifierManager 字符串ID管理部分
/// </summary>
public partial class UeIdentifierManager
{
#region 字符串ID管理方法
/// <summary>
/// 将字符串转换为ID,如果不存在则创建新的ID
/// 性能优化:使用缓存减少重复计算
/// </summary>
/// <param name="str">要转换的字符串</param>
/// <returns>对应的ID</returns>
/// <exception cref="ArgumentNullException">字符串为空时抛出</exception>
public int StringToId(string str)
{
if (string.IsNullOrEmpty(str))
{
_logger?.LogWarning("尝试转换空字符串为ID");
return 0;
}
lock (_lockObject)
{
IncrementCacheStat("StringToId");
if (_stringIdCache.TryGetValue(str, out int existingId))
{
IncrementCacheStat("CacheHits");
return existingId;
}
IncrementCacheStat("CacheMisses");
int newId = _stringList.Count + 1;
_stringIdCache[str] = newId;
_stringList.Add(str);
_logger?.LogDebug("创建新的字符串ID映射: '{String}' -> {Id}", str, newId);
return newId;
}
}
/// <summary>
/// 根据ID获取对应的字符串
/// 性能优化:使用缓存减少重复计算
/// </summary>
/// <param name="id">字符串ID</param>
/// <returns>对应的字符串,如果ID无效则返回空字符串</returns>
public string IdToString(int id)
{
if (id <= 0)
{
_logger?.LogWarning("尝试获取无效ID的字符串: {Id}", id);
return string.Empty;
}
lock (_lockObject)
{
IncrementCacheStat("IdToString");
if (id <= _stringList.Count)
{
IncrementCacheStat("CacheHits");
return _stringList[id - 1];
}
IncrementCacheStat("CacheMisses");
_logger?.LogWarning("ID超出范围: {Id}, 最大ID: {MaxId}", id, _stringList.Count);
return string.Empty;
}
}
/// <summary>
/// 批量转换字符串为ID
/// 性能优化:批量处理减少锁开销
/// </summary>
/// <param name="strings">字符串列表</param>
/// <returns>对应的ID列表</returns>
public List<int> StringToIdBatch(IEnumerable<string> strings)
{
if (strings == null)
{
throw new ArgumentNullException(nameof(strings));
}
var results = new List<int>();
lock (_lockObject)
{
foreach (var str in strings)
{
results.Add(StringToId(str));
}
}
_logger?.LogDebug("批量转换字符串为ID,共处理 {Count} 个字符串", results.Count);
return results;
}
/// <summary>
/// 获取字符串缓存信息
/// </summary>
/// <returns>缓存信息</returns>
public (int CacheSize, int ListSize) GetStringCacheInfo()
{
lock (_lockObject)
{
return (_stringIdCache.Count, _stringList.Count);
}
}
#endregion
#region 私有辅助方法
/// <summary>
/// 增加缓存统计计数
/// </summary>
/// <param name="statName">统计名称</param>
private void IncrementCacheStat(string statName)
{
if (_cacheStats.ContainsKey(statName))
{
_cacheStats[statName]++;
}
}
#endregion
}
}

261
CoreAgent.ProtocolClient/Context/UeStateManager/TmsiMatching.cs

@ -0,0 +1,261 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using CoreAgent.ProtocolClient.Models;
using Microsoft.Extensions.Logging;
namespace CoreAgent.ProtocolClient.Context.UeStateManager
{
/// <summary>
/// UeIdentifierManager TMSI匹配部分
/// </summary>
public partial class UeIdentifierManager
{
#region TMSI匹配方法
/// <summary>
/// 根据TMSI匹配生成请求UE ID和接收UE ID的对应关系
/// 使用TmsiMatchProcessor处理匹配逻辑
/// </summary>
/// <returns>TMSI匹配结果列表</returns>
public List<TmsiMatchResult> GenerateTmsiMatches()
{
try
{
var processor = new TmsiMatchProcessor(TmsiToUeId, RequestTmsiToUeId, ImsiToUeId);
var results = processor.GenerateTmsiMatches();
_logger?.LogDebug("生成TMSI匹配结果,共 {Count} 个匹配", results.Count);
return results;
}
catch (Exception ex)
{
_logger?.LogError(ex, "生成TMSI匹配失败");
throw new GeneralUeIdentifierManagerException("GenerateTmsiMatches", "生成TMSI匹配失败", ex);
}
}
/// <summary>
/// 根据UE ID获取对应的IMSI
/// 优先查找当前UE ID,如果未找到则查找整个UE链中的IMSI
/// </summary>
/// <param name="ueId">UE ID</param>
/// <returns>对应的IMSI,如果未找到则返回空字符串</returns>
private string GetImsiForUeId(int ueId)
{
if (ueId <= 0)
{
_logger?.LogWarning("尝试获取无效UE ID的IMSI: {UeId}", ueId);
return string.Empty;
}
try
{
// 1. 首先查找当前UE ID对应的IMSI
var currentImsiMapping = ImsiToUeId.FirstOrDefault(kvp => kvp.Value.Contains(ueId));
if (!string.IsNullOrEmpty(currentImsiMapping.Key))
{
return currentImsiMapping.Key;
}
// 2. 如果当前UE ID没有IMSI,查找整个UE链中的IMSI
var anyImsiMapping = ImsiToUeId.FirstOrDefault(kvp => !string.IsNullOrEmpty(kvp.Key));
return anyImsiMapping.Key ?? string.Empty;
}
catch (Exception ex)
{
_logger?.LogError(ex, "获取UE {UeId} 的IMSI失败", ueId);
return string.Empty;
}
}
/// <summary>
/// 获取UE链的统计信息
/// </summary>
/// <returns>UE链统计信息</returns>
public UeChainStats GetUeChainStats()
{
try
{
var processor = new TmsiMatchProcessor(TmsiToUeId, RequestTmsiToUeId, ImsiToUeId);
var stats = processor.GetUeChainStats();
_logger?.LogDebug("获取UE链统计信息成功");
return stats;
}
catch (Exception ex)
{
_logger?.LogError(ex, "获取UE链统计信息失败");
throw new GeneralUeIdentifierManagerException("GetUeChainStats", "获取UE链统计信息失败", ex);
}
}
/// <summary>
/// 获取所有UE链的详细信息
/// </summary>
/// <returns>UE链详细信息列表</returns>
public List<UeChainInfo> GetUeChainDetails()
{
try
{
var processor = new TmsiMatchProcessor(TmsiToUeId, RequestTmsiToUeId, ImsiToUeId);
var details = processor.GetUeChainDetails();
_logger?.LogDebug("获取UE链详细信息成功,共 {Count} 个链", details.Count);
return details;
}
catch (Exception ex)
{
_logger?.LogError(ex, "获取UE链详细信息失败");
throw new GeneralUeIdentifierManagerException("GetUeChainDetails", "获取UE链详细信息失败", ex);
}
}
/// <summary>
/// 将 SrTmsiToUeId 平铺成 SrTmsiMapping 列表,并与 GenerateTmsiMatches 结果匹配更新 IMSI、Root UE ID 和 PLMN
/// 性能优化:使用LINQ提高查找效率
/// </summary>
/// <returns>平铺后的 SrTmsiMapping 列表,包含匹配的 IMSI、Root UE ID 和 PLMN 信息</returns>
public List<SrTmsiMapping> GenerateSrTmsiMappings(List<TmsiMatchResult> tmsiMatches)
{
if (tmsiMatches == null)
{
throw new ArgumentNullException(nameof(tmsiMatches));
}
try
{
var srTmsiMappings = new List<SrTmsiMapping>();
// 1. 平铺 SrTmsiToUeId 字典数据
foreach (var kvp in SrTmsiToUeId)
{
uint tmsi = kvp.Key;
var ueIds = kvp.Value;
foreach (int ueId in ueIds)
{
var mapping = new SrTmsiMapping(string.Empty, tmsi, ueId);
srTmsiMappings.Add(mapping);
}
}
// 2. 获取 UE 链信息以获取最外层的 Root UE ID
var ueChainDetails = GetUeChainDetails();
// 3. 根据 TMSI 匹配结果更新 SrTmsiMapping 的 IMSI 和 Root UE ID
foreach (var srMapping in srTmsiMappings)
{
UpdateSrTmsiMapping(srMapping, tmsiMatches, ueChainDetails);
}
// 4. 根据 RootUeId 从 PlmnToUeId 映射表更新 PLMN 信息
foreach (var srMapping in srTmsiMappings)
{
UpdateSrTmsiMappingPlmn(srMapping, ueChainDetails);
}
_logger?.LogDebug("生成Service Request TMSI映射成功,共 {Count} 个映射", srTmsiMappings.Count);
return srTmsiMappings;
}
catch (Exception ex)
{
_logger?.LogError(ex, "生成Service Request TMSI映射失败");
throw new GeneralUeIdentifierManagerException("GenerateSrTmsiMappings", "生成Service Request TMSI映射失败", ex);
}
}
#endregion
#region 私有辅助方法
/// <summary>
/// 更新 SrTmsiMapping 的 IMSI 和 Root UE ID
/// </summary>
/// <param name="srMapping">Service Request TMSI映射</param>
/// <param name="tmsiMatches">TMSI匹配结果</param>
/// <param name="ueChainDetails">UE链详细信息</param>
private void UpdateSrTmsiMapping(SrTmsiMapping srMapping, List<TmsiMatchResult> tmsiMatches, List<UeChainInfo> ueChainDetails)
{
// 查找匹配的 TMSI 结果
var matchingResult = tmsiMatches.FirstOrDefault(match => match.Tmsi == srMapping.Tmsi);
if (matchingResult != null && !string.IsNullOrEmpty(matchingResult.Imsi))
{
// 更新 IMSI
srMapping.Imsi = matchingResult.Imsi;
// 查找对应的最外层 Root UE ID
var chainInfo = ueChainDetails.FirstOrDefault(chain =>
chain.UeIds.Contains(srMapping.UeId));
if (chainInfo != null)
{
srMapping.RootUeId = chainInfo.RootUeId;
}
else
{
// 如果当前 UE ID 不在任何链中,尝试从匹配结果中查找
chainInfo = ueChainDetails.FirstOrDefault(chain =>
chain.UeIds.Contains(matchingResult.RequestUeId) ||
chain.UeIds.Contains(matchingResult.ReceiveUeId));
if (chainInfo != null)
{
srMapping.RootUeId = chainInfo.RootUeId;
}
}
}
else
{
// 如果 TMSI 匹配中没有找到,尝试从 ImsiToUeId 映射中查找
string imsi = GetImsiForUeId(srMapping.UeId);
if (!string.IsNullOrEmpty(imsi))
{
srMapping.Imsi = imsi;
// 查找对应的最外层 Root UE ID
var chainInfo = ueChainDetails.FirstOrDefault(chain =>
chain.UeIds.Contains(srMapping.UeId));
if (chainInfo != null)
{
srMapping.RootUeId = chainInfo.RootUeId;
}
}
}
}
/// <summary>
/// 更新 SrTmsiMapping 的 PLMN 信息
/// </summary>
/// <param name="srMapping">Service Request TMSI映射</param>
private void UpdateSrTmsiMappingPlmn(SrTmsiMapping srMapping, List<UeChainInfo> ueChainDetails)
{
if (srMapping.RootUeId > 0)
{
// 查找包含 RootUeId 的 PLMN 映射
var plmnMapping = PlmnToUeId.FirstOrDefault(kvp => kvp.Value.Contains(srMapping.RootUeId));
if (!string.IsNullOrEmpty(plmnMapping.Key))
{
srMapping.Plmn = plmnMapping.Key;
}
}
else if (srMapping.UeId > 0)
{
var Filter = TmsiToUeId.FirstOrDefault(s => s.Key == srMapping.Tmsi);
// 如果 RootUeId 为空,尝试使用 UeId 查找 PLMN
var chainInfo = ueChainDetails.FirstOrDefault(chain =>
chain.UeIds.Contains(Filter.Value));
var plmnMapping = PlmnToUeId.FirstOrDefault(kvp => kvp.Value.Contains(Filter.Value));
if (!string.IsNullOrEmpty(plmnMapping.Key))
{
srMapping.Plmn = plmnMapping.Key;
srMapping.RootUeId = chainInfo.RootUeId;
}
}
}
#endregion
}
}

126
CoreAgent.ProtocolClient/Context/UeStateManager/UeIdentifierException.cs

@ -0,0 +1,126 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CoreAgent.ProtocolClient.Context.UeStateManager
{
/// <summary>
/// UeIdentifierManager 相关异常基类
/// </summary>
public abstract class UeIdentifierManagerException : Exception
{
protected UeIdentifierManagerException(string message) : base(message) { }
protected UeIdentifierManagerException(string message, Exception innerException) : base(message, innerException) { }
}
/// <summary>
/// 标识符格式异常
/// </summary>
public class IdentifierFormatException : UeIdentifierManagerException
{
public string Identifier { get; }
public string ExpectedFormat { get; }
public IdentifierFormatException(string identifier, string expectedFormat, string message = null)
: base(message ?? $"标识符 '{identifier}' 格式无效,期望格式: {expectedFormat}")
{
Identifier = identifier;
ExpectedFormat = expectedFormat;
}
}
/// <summary>
/// UE信息异常
/// </summary>
public class UeInfoException : UeIdentifierManagerException
{
public int UeId { get; }
public UeInfoException(int ueId, string message)
: base(message)
{
UeId = ueId;
}
}
/// <summary>
/// 映射关系异常
/// </summary>
public class MappingException : UeIdentifierManagerException
{
public string MappingType { get; }
public object Key { get; }
public MappingException(string mappingType, object key, string message)
: base(message)
{
MappingType = mappingType;
Key = key;
}
}
/// <summary>
/// 缓存异常
/// </summary>
public class CacheException : UeIdentifierManagerException
{
public string CacheType { get; }
public CacheException(string cacheType, string message)
: base(message)
{
CacheType = cacheType;
}
public CacheException(string cacheType, string message, Exception innerException)
: base(message, innerException)
{
CacheType = cacheType;
}
}
/// <summary>
/// 解析异常
/// </summary>
public class ParsingException : UeIdentifierManagerException
{
public string ParsingType { get; }
public string Input { get; }
public ParsingException(string parsingType, string input, string message)
: base(message)
{
ParsingType = parsingType;
Input = input;
}
public ParsingException(string parsingType, string input, string message, Exception innerException)
: base(message, innerException)
{
ParsingType = parsingType;
Input = input;
}
}
/// <summary>
/// 通用UE标识符管理器异常
/// </summary>
public class GeneralUeIdentifierManagerException : UeIdentifierManagerException
{
public string Operation { get; }
public GeneralUeIdentifierManagerException(string operation, string message)
: base(message)
{
Operation = operation;
}
public GeneralUeIdentifierManagerException(string operation, string message, Exception innerException)
: base(message, innerException)
{
Operation = operation;
}
}
}

205
CoreAgent.ProtocolClient/Context/UeStateManager/UeIdentifierManager.cs

@ -0,0 +1,205 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using CoreAgent.ProtocolClient.Enums;
using CoreAgent.ProtocolClient.Models;
using Microsoft.Extensions.Logging;
namespace CoreAgent.ProtocolClient.Context.UeStateManager
{
/// <summary>
/// UE标识符管理类
/// 负责管理UE信息、标识符映射、时间戳等状态
/// 优化版本:增强异常处理、性能优化、代码拆分
/// </summary>
public partial class UeIdentifierManager
{
#region 公共属性 - 映射表
/// <summary>
/// TMSI到UE ID的映射表
/// </summary>
public Dictionary<uint, int> TmsiToUeId { get; set; } = new();
/// <summary>
/// 请求TMSI到UE ID的映射表
/// </summary>
public Dictionary<uint, int> RequestTmsiToUeId { get; set; } = new();
/// <summary>
/// IMSI到UE ID的映射表
/// </summary>
public Dictionary<string, List<int>> ImsiToUeId { get; set; } = new();
/// <summary>
/// PLMN到UE ID的映射表
/// </summary>
public Dictionary<string, List<int>> PlmnToUeId { get; set; } = new();
/// <summary>
/// Service Request TMSI到UE ID的映射表
/// </summary>
public Dictionary<uint, List<int>> SrTmsiToUeId { get; set; } = new();
/// <summary>
/// RNTI到UE ID的映射表
/// </summary>
public Dictionary<int, int> RntiToUeId { get; set; } = new();
/// <summary>
/// UE信息列表,以UE ID为键
/// </summary>
public Dictionary<int, UEInfo> UeList { get; set; } = new();
#endregion
#region 公共属性 - 时间戳和小区信息
/// <summary>
/// 最后处理的时间戳
/// </summary>
public long LastTimestamp { get; set; }
/// <summary>
/// 时间戳偏移量
/// </summary>
public long TimestampOffset { get; set; }
/// <summary>
/// 最后处理的小区ID
/// </summary>
public int? LastCell { get; set; }
#endregion
#region 私有字段 - 字符串缓存和性能优化
/// <summary>
/// 字符串到ID的映射缓存
/// </summary>
private readonly Dictionary<string, int> _stringIdCache = new();
/// <summary>
/// ID到字符串的列表缓存
/// </summary>
private readonly List<string> _stringList = new();
/// <summary>
/// 日志记录器
/// </summary>
private readonly ILogger<UeIdentifierManager> _logger;
/// <summary>
/// 缓存统计信息
/// </summary>
private readonly Dictionary<string, int> _cacheStats = new();
/// <summary>
/// 线程锁对象
/// </summary>
private readonly object _lockObject = new object();
#endregion
#region 构造函数
/// <summary>
/// 初始化UE标识符管理器
/// </summary>
/// <param name="logger">日志记录器</param>
public UeIdentifierManager(ILogger<UeIdentifierManager> logger)
{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
InitializeProtocolTypeStrings();
InitializeCacheStats();
}
#endregion
#region 私有初始化方法
/// <summary>
/// 初始化协议类型字符串缓存
/// </summary>
private void InitializeProtocolTypeStrings()
{
try
{
string[] protocolTypeNames = Enum.GetNames(typeof(LogChannelId));
foreach (var protocolTypeName in protocolTypeNames)
{
StringToId(protocolTypeName);
}
_logger.LogDebug("协议类型字符串缓存初始化完成,共 {Count} 个类型", protocolTypeNames.Length);
}
catch (Exception ex)
{
_logger.LogError(ex, "初始化协议类型字符串缓存失败");
throw new CacheException("ProtocolTypeStrings", "初始化协议类型字符串缓存失败", ex);
}
}
/// <summary>
/// 初始化缓存统计信息
/// </summary>
private void InitializeCacheStats()
{
_cacheStats["StringToId"] = 0;
_cacheStats["IdToString"] = 0;
_cacheStats["CacheHits"] = 0;
_cacheStats["CacheMisses"] = 0;
}
#endregion
#region 公共方法 - 统计和监控
/// <summary>
/// 获取缓存统计信息
/// </summary>
/// <returns>缓存统计信息字典</returns>
public Dictionary<string, int> GetCacheStats()
{
lock (_lockObject)
{
return new Dictionary<string, int>(_cacheStats);
}
}
/// <summary>
/// 获取映射表统计信息
/// </summary>
/// <returns>映射表统计信息</returns>
public Dictionary<string, int> GetMappingStats()
{
return new Dictionary<string, int>
{
["TmsiToUeId"] = TmsiToUeId.Count,
["RequestTmsiToUeId"] = RequestTmsiToUeId.Count,
["ImsiToUeId"] = ImsiToUeId.Count,
["PlmnToUeId"] = PlmnToUeId.Count,
["SrTmsiToUeId"] = SrTmsiToUeId.Count,
["RntiToUeId"] = RntiToUeId.Count,
["UeList"] = UeList.Count
};
}
/// <summary>
/// 清理缓存
/// </summary>
public void ClearCache()
{
lock (_lockObject)
{
_stringIdCache.Clear();
_stringList.Clear();
InitializeCacheStats();
_logger.LogInformation("字符串缓存已清理");
}
}
#endregion
}
}

271
CoreAgent.ProtocolClient/Context/UeStateManager/UeIdentifierMapping.cs

@ -0,0 +1,271 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using CoreAgent.ProtocolClient.Enums;
using CoreAgent.ProtocolClient.Models;
using Microsoft.Extensions.Logging;
namespace CoreAgent.ProtocolClient.Context.UeStateManager
{
/// <summary>
/// UeIdentifierManager UE标识符映射部分
/// </summary>
public partial class UeIdentifierManager
{
#region UE标识符映射方法
/// <summary>
/// 设置IMSI与UE ID的映射关系
/// 性能优化:使用HashSet提高查找效率
/// </summary>
/// <param name="ueId">UE ID</param>
/// <param name="imsi">IMSI标识符</param>
/// <exception cref="UeInfoException">UE ID无效时抛出</exception>
public void SetImsi(int ueId, string imsi)
{
if (string.IsNullOrEmpty(imsi))
{
_logger?.LogWarning("尝试设置空的IMSI");
return;
}
if (ueId <= 0)
{
throw new UeInfoException(ueId, "UE ID必须大于0");
}
try
{
lock (_lockObject)
{
if (!ImsiToUeId.TryGetValue(imsi, out var existingUeIds))
{
ImsiToUeId[imsi] = new List<int> { ueId };
_logger?.LogDebug("创建新的IMSI映射: {Imsi} -> UE {UeId}", imsi, ueId);
}
else if (!existingUeIds.Contains(ueId))
{
existingUeIds.Add(ueId);
_logger?.LogDebug("添加IMSI映射: {Imsi} -> UE {UeId}", imsi, ueId);
}
}
}
catch (Exception ex)
{
_logger?.LogError(ex, "设置IMSI映射失败: UE {UeId}, IMSI {Imsi}", ueId, imsi);
throw new MappingException("IMSI", imsi, $"设置IMSI映射失败: {ex.Message}");
}
}
/// <summary>
/// 设置TMSI与UE ID的映射关系
/// 性能优化:使用缓存减少重复转换
/// </summary>
/// <param name="ueId">UE ID</param>
/// <param name="tmsi">TMSI标识符(十六进制字符串)</param>
/// <exception cref="IdentifierFormatException">TMSI格式无效时抛出</exception>
public void SetTmsi(int ueId, string tmsi)
{
if (string.IsNullOrEmpty(tmsi))
{
_logger?.LogWarning("尝试设置空的TMSI");
return;
}
if (ueId <= 0)
{
throw new UeInfoException(ueId, "UE ID必须大于0");
}
try
{
uint tmsiKey = Convert.ToUInt32(tmsi, 16);
lock (_lockObject)
{
if (TmsiToUeId.TryGetValue(tmsiKey, out int existingUeId) && existingUeId > 0)
{
// 同步IMSI信息
SyncImsiFromExistingUe(existingUeId, ueId);
// 合并UE信息
MergeUeInfo(existingUeId, ueId);
_logger?.LogDebug("更新TMSI映射: {Tmsi} -> UE {UeId} (原UE {ExistingUeId})", tmsi, ueId, existingUeId);
}
else
{
TmsiToUeId[tmsiKey] = ueId;
_logger?.LogDebug("创建新的TMSI映射: {Tmsi} -> UE {UeId}", tmsi, ueId);
}
}
}
catch (FormatException ex)
{
var errorMessage = $"TMSI格式无效: '{tmsi}',期望十六进制格式";
_logger?.LogError(ex, errorMessage);
throw new IdentifierFormatException(tmsi, "十六进制格式", errorMessage);
}
catch (Exception ex)
{
_logger?.LogError(ex, "设置TMSI映射失败: UE {UeId}, TMSI {Tmsi}", ueId, tmsi);
throw new MappingException("TMSI", tmsi, $"设置TMSI映射失败: {ex.Message}");
}
}
/// <summary>
/// 设置请求TMSI与UE ID的映射关系
/// </summary>
/// <param name="ueId">UE ID</param>
/// <param name="tmsi">请求TMSI标识符(十六进制字符串)</param>
public void SetRequestTmsi(int ueId, string tmsi)
{
if (string.IsNullOrEmpty(tmsi))
{
_logger?.LogWarning("尝试设置空的请求TMSI");
return;
}
if (ueId <= 0)
{
throw new UeInfoException(ueId, "UE ID必须大于0");
}
try
{
uint tmsiKey = Convert.ToUInt32(tmsi, 16);
lock (_lockObject)
{
RequestTmsiToUeId[tmsiKey] = ueId;
_logger?.LogDebug("设置请求TMSI映射: {Tmsi} -> UE {UeId}", tmsi, ueId);
}
}
catch (FormatException ex)
{
var errorMessage = $"请求TMSI格式无效: '{tmsi}',期望十六进制格式";
_logger?.LogError(ex, errorMessage);
throw new IdentifierFormatException(tmsi, "十六进制格式", errorMessage);
}
}
/// <summary>
/// 设置RNTI与UE ID的映射关系
/// </summary>
/// <param name="log">协议日志</param>
/// <param name="rnti">RNTI标识符(十六进制字符串)</param>
public void SetRnti(BuildProtocolLog log, string rnti)
{
if (log?.UeId == null || string.IsNullOrEmpty(rnti))
{
_logger?.LogWarning("设置RNTI映射失败:日志或RNTI为空");
return;
}
try
{
int rntiId = int.Parse(rnti, System.Globalization.NumberStyles.HexNumber);
lock (_lockObject)
{
RntiToUeId[rntiId] = log.UeId.Value;
_logger?.LogDebug("设置RNTI映射: {Rnti} -> UE {UeId}", rnti, log.UeId.Value);
}
}
catch (FormatException ex)
{
var errorMessage = $"RNTI格式无效: '{rnti}',期望十六进制格式";
_logger?.LogError(ex, errorMessage);
throw new IdentifierFormatException(rnti, "十六进制格式", errorMessage);
}
}
/// <summary>
/// 设置PLMN与UE ID的映射关系
/// </summary>
/// <param name="ueId">UE ID</param>
/// <param name="plmn">PLMN标识符</param>
public void SetPlmn(int ueId, string plmn)
{
if (string.IsNullOrEmpty(plmn))
{
_logger?.LogWarning("尝试设置空的PLMN");
return;
}
if (ueId <= 0)
{
throw new UeInfoException(ueId, "UE ID必须大于0");
}
try
{
lock (_lockObject)
{
if (!PlmnToUeId.TryGetValue(plmn, out var existingUeIds))
{
PlmnToUeId[plmn] = new List<int> { ueId };
_logger?.LogDebug("创建新的PLMN映射: {Plmn} -> UE {UeId}", plmn, ueId);
}
else if (!existingUeIds.Contains(ueId))
{
existingUeIds.Add(ueId);
_logger?.LogDebug("添加PLMN映射: {Plmn} -> UE {UeId}", plmn, ueId);
}
}
}
catch (Exception ex)
{
_logger?.LogError(ex, "设置PLMN映射失败: UE {UeId}, PLMN {Plmn}", ueId, plmn);
throw new MappingException("PLMN", plmn, $"设置PLMN映射失败: {ex.Message}");
}
}
/// <summary>
/// 设置Service Request TMSI与UE ID的映射关系
/// </summary>
/// <param name="ueId">UE ID</param>
/// <param name="srTmsi">Service Request TMSI标识符(十六进制字符串)</param>
public void SetSrTmsi(int ueId, string srTmsi)
{
if (string.IsNullOrEmpty(srTmsi))
{
_logger?.LogWarning("尝试设置空的Service Request TMSI");
return;
}
if (ueId <= 0)
{
throw new UeInfoException(ueId, "UE ID必须大于0");
}
try
{
uint srTmsiKey = Convert.ToUInt32(srTmsi, 16);
lock (_lockObject)
{
if (!SrTmsiToUeId.TryGetValue(srTmsiKey, out var existingUeIds))
{
SrTmsiToUeId[srTmsiKey] = new List<int> { ueId };
_logger?.LogDebug("创建新的Service Request TMSI映射: {SrTmsi} -> UE {UeId}", srTmsi, ueId);
}
else if (!existingUeIds.Contains(ueId))
{
existingUeIds.Add(ueId);
_logger?.LogDebug("添加Service Request TMSI映射: {SrTmsi} -> UE {UeId}", srTmsi, ueId);
}
}
}
catch (FormatException ex)
{
var errorMessage = $"Service Request TMSI格式无效: '{srTmsi}',期望十六进制格式";
_logger?.LogError(ex, errorMessage);
throw new IdentifierFormatException(srTmsi, "十六进制格式", errorMessage);
}
}
#endregion
}
}

244
CoreAgent.ProtocolClient/Context/UeStateManager/UeInfoManagement.cs

@ -0,0 +1,244 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using CoreAgent.ProtocolClient.Models;
using Microsoft.Extensions.Logging;
namespace CoreAgent.ProtocolClient.Context.UeStateManager
{
/// <summary>
/// UeIdentifierManager UE信息管理部分
/// </summary>
public partial class UeIdentifierManager
{
#region UE信息管理方法
/// <summary>
/// 合并两个UE的信息
/// 性能优化:减少不必要的对象创建
/// </summary>
/// <param name="ueId1">第一个UE ID</param>
/// <param name="ueId2">第二个UE ID</param>
public void MergeUeInfo(int ueId1, int ueId2)
{
if (ueId1 <= 0 || ueId2 <= 0)
{
throw new UeInfoException(Math.Min(ueId1, ueId2), "UE ID必须大于0");
}
try
{
lock (_lockObject)
{
UeList.TryGetValue(ueId1, out var ue1);
UeList.TryGetValue(ueId2, out var ue2);
if (ReferenceEquals(ue1, ue2))
{
if (ue2 == null)
{
ue2 = CreateUeInfo(ueId2);
UeList[ueId1] = ue2;
}
}
else if (ue1 == null)
{
UeList[ueId1] = ue2;
}
else if (ue2 == null)
{
UeList[ueId2] = ue1;
SyncPlmnFromExistingUe(ueId1, ueId2);
}
else
{
UeList[ueId2] = ue1;
SyncPlmnFromExistingUe(ueId1, ueId2);
}
_logger?.LogDebug("合并UE信息: UE {UeId1} 和 UE {UeId2}", ueId1, ueId2);
}
}
catch (Exception ex)
{
_logger?.LogError(ex, "合并UE信息失败: UE {UeId1} 和 UE {UeId2}", ueId1, ueId2);
throw new UeInfoException(Math.Min(ueId1, ueId2), $"合并UE信息失败: {ex.Message}");
}
}
/// <summary>
/// 创建新的UE信息
/// </summary>
/// <param name="ueId">UE ID</param>
/// <returns>创建的UE信息</returns>
private UEInfo CreateUeInfo(int ueId)
{
var ueInfo = new UEInfo
{
UeId = ueId,
Caps = new ProtocolCaps { UeId = ueId }
};
UeList[ueId] = ueInfo;
_logger?.LogDebug("创建新的UE信息: UE {UeId}", ueId);
return ueInfo;
}
/// <summary>
/// 获取UE信息
/// </summary>
/// <param name="ueId">UE ID</param>
/// <returns>UE信息,如果不存在则返回null</returns>
public UEInfo? GetUeInfo(int ueId)
{
if (ueId <= 0)
{
_logger?.LogWarning("尝试获取无效UE ID的信息: {UeId}", ueId);
return null;
}
lock (_lockObject)
{
return UeList.TryGetValue(ueId, out var ueInfo) ? ueInfo : null;
}
}
/// <summary>
/// 获取UE能力信息,如果不存在则创建
/// 同时根据ImsiToUeId映射表更新IMSI信息
/// </summary>
/// <param name="ueId">UE ID</param>
/// <returns>UE能力信息</returns>
public ProtocolCaps GetUeCaps(int ueId)
{
if (ueId <= 0)
{
throw new UeInfoException(ueId, "UE ID必须大于0");
}
lock (_lockObject)
{
if (!UeList.TryGetValue(ueId, out var ueInfo) || ueInfo == null)
{
ueInfo = CreateUeInfo(ueId);
}
if (ueInfo.Caps == null)
{
ueInfo.Caps = new ProtocolCaps { UeId = ueId };
}
// 根据ImsiToUeId映射表更新IMSI信息
UpdateImsiFromMapping(ueId, ueInfo);
return ueInfo.Caps;
}
}
/// <summary>
/// 根据ImsiToUeId映射表更新UE的IMSI信息
/// 性能优化:使用LINQ提高查找效率
/// </summary>
/// <param name="ueId">UE ID</param>
/// <param name="ueInfo">UE信息对象</param>
private void UpdateImsiFromMapping(int ueId, UEInfo ueInfo)
{
var imsiMapping = ImsiToUeId.FirstOrDefault(kvp => kvp.Value.Contains(ueId));
if (!string.IsNullOrEmpty(imsiMapping.Key))
{
ueInfo.Imsi = imsiMapping.Key;
_logger?.LogDebug("更新UE {UeId} 的IMSI信息: {Imsi}", ueId, imsiMapping.Key);
}
}
/// <summary>
/// 从现有UE ID同步IMSI信息到新的UE ID
/// </summary>
/// <param name="existingUeId">现有UE ID</param>
/// <param name="newUeId">新的UE ID</param>
private void SyncImsiFromExistingUe(int existingUeId, int newUeId)
{
var imsiMapping = ImsiToUeId.FirstOrDefault(kvp => kvp.Value.Contains(existingUeId));
if (!string.IsNullOrEmpty(imsiMapping.Key))
{
SetImsi(newUeId, imsiMapping.Key);
_logger?.LogDebug("同步IMSI信息: 从UE {ExistingUeId} 到 UE {NewUeId}: {Imsi}",
existingUeId, newUeId, imsiMapping.Key);
}
}
/// <summary>
/// 从现有UE ID同步PLMN信息到新的UE ID
/// </summary>
/// <param name="existingUeId">现有UE ID</param>
/// <param name="newUeId">新的UE ID</param>
private void SyncPlmnFromExistingUe(int existingUeId, int newUeId)
{
var plmnMapping = PlmnToUeId.FirstOrDefault(kvp => kvp.Value.Contains(existingUeId));
if (!string.IsNullOrEmpty(plmnMapping.Key))
{
SetPlmn(newUeId, plmnMapping.Key);
_logger?.LogDebug("同步PLMN信息: 从UE {ExistingUeId} 到 UE {NewUeId}: {Plmn}",
existingUeId, newUeId, plmnMapping.Key);
}
}
/// <summary>
/// 获取UeList中UEInfo.UeId与Caps.UeId组合的字典
/// </summary>
/// <returns>包含UE ID和Caps UeId组合的字典,键为UEInfo.UeId,值为Caps.UeId</returns>
public Dictionary<int, int> GetUeIdToCapsUeIdMapping()
{
lock (_lockObject)
{
var mapping = new Dictionary<int, int>();
foreach (var kvp in UeList)
{
var ueId = kvp.Key;
var ueInfo = kvp.Value;
if (ueInfo?.Caps != null)
{
mapping[ueId] = ueInfo.Caps.UeId;
}
}
return mapping;
}
}
/// <summary>
/// 批量获取UE信息
/// 性能优化:批量处理减少锁开销
/// </summary>
/// <param name="ueIds">UE ID列表</param>
/// <returns>UE信息字典</returns>
public Dictionary<int, UEInfo> GetUeInfoBatch(IEnumerable<int> ueIds)
{
if (ueIds == null)
{
throw new ArgumentNullException(nameof(ueIds));
}
var result = new Dictionary<int, UEInfo>();
lock (_lockObject)
{
foreach (var ueId in ueIds)
{
if (UeList.TryGetValue(ueId, out var ueInfo))
{
result[ueId] = ueInfo;
}
}
}
_logger?.LogDebug("批量获取UE信息,共获取 {Count} 个UE的信息", result.Count);
return result;
}
#endregion
}
}

4
CoreAgent.ProtocolClient/CoreAgent.ProtocolClient.csproj

@ -12,8 +12,4 @@
<PackageReference Include="WebSocket4Net" Version="0.15.2" />
</ItemGroup>
<ItemGroup>
<Folder Include="Enums\" />
</ItemGroup>
</Project>

509
CoreAgent.ProtocolClient/Docs/MessageIdManager.md

@ -0,0 +1,509 @@
# MessageIdManager.cs (自动转换为Markdown)
```csharp
// 以下内容为原始C#代码,含详细注释
// 文件原路径:Managers/MessageIdManager.cs
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
{
/// <summary>
/// 消息ID管理器 - 改进版
///
/// 修复的问题:
/// 1. 使用long类型防止ID溢出
/// 2. 完善消息处理器清理机制
/// 3. 改进LogGet ID管理逻辑
/// 4. 增强参数验证和异常处理
/// 5. 优化性能,减少字符串操作
/// 6. 添加线程安全保护
/// 7. 改进日志记录格式
///
/// 设计原则:
/// - 单一职责:专门负责消息ID管理
/// - 开闭原则:支持扩展不同类型的消息ID管理
/// - 线程安全:所有操作都是线程安全的
/// - 性能优化:减少不必要的内存分配
/// - 错误处理:完善的异常处理和参数验证
/// </summary>
public class MessageIdManager : IDisposable
{
#region 私有字段
private readonly ILogger _logger;
private readonly string _clientName;
private readonly ConcurrentDictionary<long, MessageHandler> _messageHandlers;
private readonly ConcurrentDictionary<string, MessageHandler> _messageHandlersByName;
// 使用long类型防止溢出
private long _generalMessageId;
private long _logGetMessageId;
// 状态管理
private bool _disposed;
// 性能优化:缓存字符串构建器
private readonly StringBuilder _logBuilder = new StringBuilder(256);
#endregion
#region 事件
/// <summary>
/// 日志获取ID变化事件
/// </summary>
public event EventHandler<LogGetIdChangedEventArgs>? LogGetIdChanged;
/// <summary>
/// 消息处理器清理事件
/// </summary>
public event EventHandler<HandlerCleanupEventArgs>? HandlerCleanup;
#endregion
#region 属性
/// <summary>
/// 当前通用消息ID
/// </summary>
public long CurrentGeneralMessageId => Interlocked.Read(ref _generalMessageId);
/// <summary>
/// 当前日志获取消息ID
/// </summary>
public long CurrentLogGetMessageId => Interlocked.Read(ref _logGetMessageId);
/// <summary>
/// 是否已释放
/// </summary>
public bool IsDisposed => _disposed;
/// <summary>
/// 消息处理器数量
/// </summary>
public int MessageHandlerCount => _messageHandlers.Count;
/// <summary>
/// 按名称的消息处理器数量
/// </summary>
public int NamedMessageHandlerCount => _messageHandlersByName.Count;
/// <summary>
/// 总处理器数量
/// </summary>
public int TotalHandlerCount => MessageHandlerCount + NamedMessageHandlerCount;
#endregion
#region 构造函数
/// <summary>
/// 构造函数
/// </summary>
/// <param name="clientName">客户端名称</param>
/// <param name="logger">日志记录器</param>
public MessageIdManager(string clientName, ILogger logger)
{
_clientName = clientName ?? throw new ArgumentNullException(nameof(clientName));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_messageHandlers = new ConcurrentDictionary<long, MessageHandler>();
_messageHandlersByName = new ConcurrentDictionary<string, MessageHandler>();
_generalMessageId = 0;
_logGetMessageId = -1; // 初始化为-1,表示未开始
_logger.LogInformation("[{ClientName}] 创建消息ID管理器,初始LogGet ID: {LogGetId}", _clientName, _logGetMessageId);
}
#endregion
#region 公共方法
/// <summary>
/// 生成通用消息ID
/// </summary>
/// <param name="message">消息对象</param>
/// <param name="callback">回调函数</param>
/// <param name="errorHandler">是否为错误处理器</param>
/// <returns>消息ID</returns>
public long GenerateGeneralMessageId(JObject message, Action<JObject>? 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;
}
/// <summary>
/// 生成日志获取消息ID - 改进版
/// </summary>
/// <param name="message">消息对象</param>
/// <param name="callback">回调函数</param>
/// <returns>消息ID</returns>
public long GenerateLogGetMessageId(JObject message, Action<JObject> 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;
}
/// <summary>
/// 处理消息响应 - 改进版
/// </summary>
/// <param name="response">响应消息</param>
/// <param name="errorHandler">错误处理回调</param>
/// <returns>是否找到并处理了消息处理器</returns>
public bool HandleMessageResponse(JObject response, Action<string>? errorHandler = null)
{
ThrowIfDisposed();
if (response == null)
return false;
// 检查消息处理器
var id = response["message_id"]?.Value<long>();
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;
}
/// <summary>
/// 设置消息处理器
/// </summary>
/// <param name="names">消息名称数组</param>
/// <param name="handler">处理器</param>
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));
}
/// <summary>
/// 取消消息处理器
/// </summary>
/// <param name="names">消息名称数组</param>
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));
}
/// <summary>
/// 检查是否为当前日志获取消息
/// </summary>
/// <param name="messageId">消息ID</param>
/// <returns>是否为当前日志获取消息</returns>
public bool IsCurrentLogGetMessage(long messageId)
{
var currentLogGetId = Interlocked.Read(ref _logGetMessageId);
return messageId == currentLogGetId;
}
/// <summary>
/// 重置日志获取ID
/// </summary>
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);
}
/// <summary>
/// 清理过期的消息处理器 - 改进版
/// </summary>
/// <param name="maxAge">最大年龄(毫秒)</param>
/// <returns>清理的处理器数量</returns>
public int CleanupExpiredHandlers(int maxAge = 30000) // 默认30秒
{
ThrowIfDisposed();
var now = DateTime.UtcNow;
var expiredKeys = new List<long>();
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 私有方法
/// <summary>
/// 获取下一个消息ID
/// </summary>
/// <returns>消息ID</returns>
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;
}
/// <summary>
/// 验证消息对象
/// </summary>
/// <param name="message">消息对象</param>
private void ValidateMessage(JObject message)
{
if (message == null)
throw new ArgumentNullException(nameof(message));
}
/// <summary>
/// 处理消息处理器
/// </summary>
/// <param name="id">消息ID</param>
/// <param name="handler">处理器</param>
/// <param name="response">响应消息</param>
/// <param name="errorHandler">错误处理回调</param>
/// <returns>是否处理成功</returns>
private bool HandleMessageHandler(long id, MessageHandler handler, JObject response, Action<string>? errorHandler)
{
// 如果不是通知消息,则移除处理器
if (response["notification"]?.Value<bool>() != 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;
}
/// <summary>
/// 处理按名称的消息处理器
/// </summary>
/// <param name="name">消息名称</param>
/// <param name="handler">处理器</param>
/// <param name="response">响应消息</param>
/// <returns>是否处理成功</returns>
private bool HandleNamedMessageHandler(string name, MessageHandler handler, JObject response)
{
handler.Callback?.Invoke(response);
return true;
}
/// <summary>
/// 记录LogGet消息
/// </summary>
/// <param name="id">消息ID</param>
/// <param name="message">消息对象</param>
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());
}
/// <summary>
/// 检查是否已释放
/// </summary>
private void ThrowIfDisposed()
{
if (_disposed)
{
throw new ObjectDisposedException(nameof(MessageIdManager));
}
}
#endregion
#region IDisposable
/// <summary>
/// 释放资源
/// </summary>
public void Dispose()
{
if (_disposed) return;
_disposed = true;
// 清理所有消息处理器
_messageHandlers.Clear();
_messageHandlersByName.Clear();
_logger.LogInformation("[{ClientName}] 释放消息ID管理器", _clientName);
}
#endregion
}
}
```
## 文档说明
### 类概述
`MessageIdManager` 是一个专门负责消息ID管理的类,用于替代原始实现中的 `_messageId``_logGetId` 字段,提供更统一和强大的消息ID管理功能。
### 主要功能
1. **消息ID生成**:统一管理通用消息ID和日志获取消息ID
2. **消息处理器管理**:管理消息回调处理器,支持按ID和按名称两种方式
3. **线程安全**:所有操作都是线程安全的
4. **自动清理**:支持自动清理过期的消息处理器
5. **事件通知**:提供LogGet ID变化和处理器清理的事件通知
### 设计改进
- 使用long类型防止ID溢出
- 完善的异常处理和参数验证
- 性能优化,减少字符串操作
- 标准的Dispose模式实现
- 详细的日志记录
### 使用场景
- 在WebSocket消息管理器中统一管理消息ID
- 处理消息响应和回调
- 管理日志获取流程
- 提供消息处理器的生命周期管理

29
CoreAgent.ProtocolClient/Enums/TmsiType.cs

@ -0,0 +1,29 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CoreAgent.ProtocolClient.Enums
{
/// <summary>
/// TMSI类型
/// </summary>
public enum TmsiType
{
/// <summary>
/// 接受类型
/// </summary>
Accept,
/// <summary>
/// 请求类型
/// </summary>
Request,
/// <summary>
/// 服务请求类型
/// </summary>
ServiceRequest
}
}

61
CoreAgent.ProtocolClient/Managers/MessageIdManager.cs

@ -12,29 +12,13 @@ using Newtonsoft.Json.Linq;
namespace CoreAgent.ProtocolClient.Managers
{
/// <summary>
/// 消息ID管理器 - 改进版
///
/// 修复的问题:
/// 1. 使用long类型防止ID溢出
/// 2. 完善消息处理器清理机制
/// 3. 改进LogGet ID管理逻辑
/// 4. 增强参数验证和异常处理
/// 5. 优化性能,减少字符串操作
/// 6. 添加线程安全保护
/// 7. 改进日志记录格式
///
/// 设计原则:
/// - 单一职责:专门负责消息ID管理
/// - 开闭原则:支持扩展不同类型的消息ID管理
/// - 线程安全:所有操作都是线程安全的
/// - 性能优化:减少不必要的内存分配
/// - 错误处理:完善的异常处理和参数验证
/// 消息ID管理器
/// </summary>
public class MessageIdManager : IDisposable
{
#region 私有字段
private readonly ILogger _logger;
private readonly ILogger<MessageIdManager> _logger;
private readonly string _clientName;
private readonly ConcurrentDictionary<long, MessageHandler> _messageHandlers;
private readonly ConcurrentDictionary<string, MessageHandler> _messageHandlersByName;
@ -104,9 +88,7 @@ namespace CoreAgent.ProtocolClient.Managers
/// <summary>
/// 构造函数
/// </summary>
/// <param name="clientName">客户端名称</param>
/// <param name="logger">日志记录器</param>
public MessageIdManager(string clientName, ILogger logger)
public MessageIdManager(string clientName, ILogger<MessageIdManager> logger)
{
_clientName = clientName ?? throw new ArgumentNullException(nameof(clientName));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
@ -127,10 +109,6 @@ namespace CoreAgent.ProtocolClient.Managers
/// <summary>
/// 生成通用消息ID
/// </summary>
/// <param name="message">消息对象</param>
/// <param name="callback">回调函数</param>
/// <param name="errorHandler">是否为错误处理器</param>
/// <returns>消息ID</returns>
public long GenerateGeneralMessageId(JObject message, Action<JObject>? callback = null, bool errorHandler = false)
{
ThrowIfDisposed();
@ -162,11 +140,8 @@ namespace CoreAgent.ProtocolClient.Managers
}
/// <summary>
/// 生成日志获取消息ID - 改进版
/// 生成日志获取消息ID
/// </summary>
/// <param name="message">消息对象</param>
/// <param name="callback">回调函数</param>
/// <returns>消息ID</returns>
public long GenerateLogGetMessageId(JObject message, Action<JObject> callback)
{
ThrowIfDisposed();
@ -199,11 +174,8 @@ namespace CoreAgent.ProtocolClient.Managers
}
/// <summary>
/// 处理消息响应 - 改进版
/// 处理消息响应
/// </summary>
/// <param name="response">响应消息</param>
/// <param name="errorHandler">错误处理回调</param>
/// <returns>是否找到并处理了消息处理器</returns>
public bool HandleMessageResponse(JObject response, Action<string>? errorHandler = null)
{
ThrowIfDisposed();
@ -231,8 +203,6 @@ namespace CoreAgent.ProtocolClient.Managers
/// <summary>
/// 设置消息处理器
/// </summary>
/// <param name="names">消息名称数组</param>
/// <param name="handler">处理器</param>
public void SetMessageHandler(string[] names, MessageHandler handler)
{
ThrowIfDisposed();
@ -257,7 +227,6 @@ namespace CoreAgent.ProtocolClient.Managers
/// <summary>
/// 取消消息处理器
/// </summary>
/// <param name="names">消息名称数组</param>
public void UnsetMessageHandler(string[] names)
{
ThrowIfDisposed();
@ -279,8 +248,6 @@ namespace CoreAgent.ProtocolClient.Managers
/// <summary>
/// 检查是否为当前日志获取消息
/// </summary>
/// <param name="messageId">消息ID</param>
/// <returns>是否为当前日志获取消息</returns>
public bool IsCurrentLogGetMessage(long messageId)
{
var currentLogGetId = Interlocked.Read(ref _logGetMessageId);
@ -298,10 +265,8 @@ namespace CoreAgent.ProtocolClient.Managers
}
/// <summary>
/// 清理过期的消息处理器 - 改进版
/// 清理过期的消息处理器
/// </summary>
/// <param name="maxAge">最大年龄(毫秒)</param>
/// <returns>清理的处理器数量</returns>
public int CleanupExpiredHandlers(int maxAge = 30000) // 默认30秒
{
ThrowIfDisposed();
@ -340,7 +305,6 @@ namespace CoreAgent.ProtocolClient.Managers
return cleanedCount;
}
#endregion
#region 私有方法
@ -348,7 +312,6 @@ namespace CoreAgent.ProtocolClient.Managers
/// <summary>
/// 获取下一个消息ID
/// </summary>
/// <returns>消息ID</returns>
private long GetNextMessageId()
{
var id = Interlocked.Increment(ref _generalMessageId);
@ -367,7 +330,6 @@ namespace CoreAgent.ProtocolClient.Managers
/// <summary>
/// 验证消息对象
/// </summary>
/// <param name="message">消息对象</param>
private void ValidateMessage(JObject message)
{
if (message == null)
@ -377,11 +339,6 @@ namespace CoreAgent.ProtocolClient.Managers
/// <summary>
/// 处理消息处理器
/// </summary>
/// <param name="id">消息ID</param>
/// <param name="handler">处理器</param>
/// <param name="response">响应消息</param>
/// <param name="errorHandler">错误处理回调</param>
/// <returns>是否处理成功</returns>
private bool HandleMessageHandler(long id, MessageHandler handler, JObject response, Action<string>? errorHandler)
{
// 如果不是通知消息,则移除处理器
@ -412,10 +369,6 @@ namespace CoreAgent.ProtocolClient.Managers
/// <summary>
/// 处理按名称的消息处理器
/// </summary>
/// <param name="name">消息名称</param>
/// <param name="handler">处理器</param>
/// <param name="response">响应消息</param>
/// <returns>是否处理成功</returns>
private bool HandleNamedMessageHandler(string name, MessageHandler handler, JObject response)
{
handler.Callback?.Invoke(response);
@ -425,8 +378,6 @@ namespace CoreAgent.ProtocolClient.Managers
/// <summary>
/// 记录LogGet消息
/// </summary>
/// <param name="id">消息ID</param>
/// <param name="message">消息对象</param>
private void LogLogGetMessage(long id, JObject message)
{
_logBuilder.Clear();

40
CoreAgent.ProtocolClient/Managers/WebSocketMgr/Constructor.cs

@ -15,40 +15,20 @@ namespace CoreAgent.ProtocolClient.Managers.WebSocketMgr
{
#region 构造函数
/// <summary>
/// 构造函数 - 对应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管理
/// - 移除冗余:移除了消息缓存相关的初始化
/// </summary>
/// <param name="clientName">客户端名称,对应LTEClientWebSocket._config.Name</param>
/// <param name="logger">日志记录器,对应LTEClientWebSocket._logger</param>
public WebSocketMessageManager(string clientName, ILogger logger)
public WebSocketMessageManager(string clientName, ILoggerFactory loggerFactory)
{
// 参数验证
_clientName = clientName ?? throw new ArgumentNullException(nameof(clientName));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
var factory = loggerFactory ?? throw new ArgumentNullException(nameof(loggerFactory));
// 创建MessageIdManager,替代原始的_messageId和_logGetId字段
_messageIdManager = new MessageIdManager(clientName, logger);
// 创建自己的Logger
_logger = factory.CreateLogger<WebSocketMessageManager>();
// 创建消息队列,使用BlockingCollection优化线程安全性和性能
// 初始化消息ID管理器 - 使用ILoggerFactory创建正确的logger类型
var messageIdLogger = factory.CreateLogger<MessageIdManager>();
_messageIdManager = new MessageIdManager(clientName, messageIdLogger);
// 创建消息队列
_messageFifo = new BlockingCollection<JObject>();
_logger.LogInformation($"[{_clientName}] 创建WebSocket消息管理器");

51
CoreAgent.ProtocolClient/Managers/WebSocketMgr/Dispose.cs

@ -16,27 +16,7 @@ namespace CoreAgent.ProtocolClient.Managers.WebSocketMgr
#region IDisposable实现
/// <summary>
/// 释放资源 - 对应LTEClientWebSocket.Dispose()方法
///
/// 功能说明:
/// 1. 释放WebSocket连接和相关资源
/// 2. 清理消息队列和定时器
/// 3. 设置释放标志,防止重复释放
/// 4. 调用MessageIdManager的Dispose方法
///
/// 对应关系:
/// - 资源释放:对应原始实现中的Dispose()方法
/// - 连接关闭:对应原始实现中的WebSocket关闭逻辑
/// - 定时器清理:对应原始实现中的定时器释放逻辑
/// - 队列清理:对应原始实现中的队列清理逻辑
/// - 释放标志:对应原始实现中的_disposed设置
/// - 日志记录:对应原始实现中的释放日志记录
///
/// 重构改进:
/// - 更清晰的资源释放顺序
/// - 更完善的异常处理
/// - 更详细的日志记录
/// - 保持了完全一致的释放逻辑
/// 释放资源
/// </summary>
public void Dispose()
{
@ -45,25 +25,8 @@ namespace CoreAgent.ProtocolClient.Managers.WebSocketMgr
}
/// <summary>
/// 释放资源的受保护方法 - 实现标准的Dispose模式
///
/// 功能说明:
/// 1. 实现标准的Dispose模式,支持手动释放和垃圾回收
/// 2. 确保资源只被释放一次
/// 3. 按照正确的顺序释放资源
///
/// 对应关系:
/// - 释放模式:对应.NET标准的Dispose模式
/// - 资源清理:对应原始实现中的资源清理逻辑
/// - 异常处理:对应原始实现中的异常处理
///
/// 重构改进:
/// - 标准的Dispose模式实现
/// - 更安全的资源管理
/// - 更好的异常处理
/// - 保持了完全一致的清理逻辑
/// 释放资源的受保护方法
/// </summary>
/// <param name="disposing">是否为手动释放</param>
protected virtual void Dispose(bool disposing)
{
if (_disposed)
@ -75,23 +38,23 @@ namespace CoreAgent.ProtocolClient.Managers.WebSocketMgr
{
_logger.LogInformation($"[{_clientName}] 释放WebSocket消息管理器资源");
// 停止消息发送定时器 - 对应原始实现中的定时器释放
// 停止消息发送定时器
StopMessageDeferTimer();
// 清空消息队列 - 对应原始实现中的队列清理
// 清空消息队列
ClearMessageQueue();
// 释放BlockingCollection资源 - 优化:确保BlockingCollection正确释放
// 释放BlockingCollection资源
_messageFifo?.Dispose();
// 关闭WebSocket连接 - 对应原始实现中的WebSocket关闭
// 关闭WebSocket连接
if (_webSocket != null)
{
_webSocket.Close();
_webSocket = null;
}
// 释放MessageIdManager - 对应原始实现中的相关资源释放
// 释放MessageIdManager
_messageIdManager?.Dispose();
}
catch (Exception ex)

203
CoreAgent.ProtocolClient/Managers/WebSocketMgr/PrivateMethods.cs

@ -16,22 +16,7 @@ namespace CoreAgent.ProtocolClient.Managers.WebSocketMgr
#region 私有方法
/// <summary>
/// 检查对象是否已释放,如果已释放则抛出异常
///
/// 功能说明:
/// 1. 检查_disposed字段,如果为true则抛出ObjectDisposedException
/// 2. 在所有公共方法开始时调用,确保对象状态正确
/// 3. 提供统一的释放状态检查逻辑
///
/// 对应关系:
/// - 检查逻辑:对应原始实现中的_disposed检查
/// - 异常类型:ObjectDisposedException,与.NET标准一致
/// - 使用场景:在所有公共方法开始时调用
///
/// 重构改进:
/// - 统一的释放状态检查
/// - 更清晰的异常信息
/// - 更好的代码复用
/// 检查对象是否已释放
/// </summary>
private void ThrowIfDisposed()
{
@ -40,25 +25,8 @@ namespace CoreAgent.ProtocolClient.Managers.WebSocketMgr
}
/// <summary>
/// WebSocket连接打开事件处理器 - 对应LTEClientWebSocket.OnSocketOpened
///
/// 功能说明:
/// 1. 处理WebSocket连接成功建立事件
/// 2. 记录连接成功日志
/// 3. 触发ConnectionOpened事件
///
/// 对应关系:
/// - 事件处理:对应原始实现中的OnSocketOpened方法
/// - 日志记录:对应原始实现中的连接成功日志
/// - 事件触发:对应原始实现中的ConnectionOpened事件触发
///
/// 重构改进:
/// - 更清晰的日志记录
/// - 更好的异常处理
/// - 保持了完全一致的事件处理逻辑
/// WebSocket连接打开事件处理器
/// </summary>
/// <param name="sender">事件发送者</param>
/// <param name="e">事件参数</param>
private void OnSocketOpened(object? sender, EventArgs e)
{
try
@ -73,25 +41,8 @@ namespace CoreAgent.ProtocolClient.Managers.WebSocketMgr
}
/// <summary>
/// WebSocket连接关闭事件处理器 - 对应LTEClientWebSocket.OnSocketClosed
///
/// 功能说明:
/// 1. 处理WebSocket连接关闭事件
/// 2. 记录连接关闭日志
/// 3. 触发ConnectionClosed事件
///
/// 对应关系:
/// - 事件处理:对应原始实现中的OnSocketClosed方法
/// - 日志记录:对应原始实现中的连接关闭日志
/// - 事件触发:对应原始实现中的ConnectionClosed事件触发
///
/// 重构改进:
/// - 更清晰的日志记录
/// - 更好的异常处理
/// - 保持了完全一致的事件处理逻辑
/// WebSocket连接关闭事件处理器
/// </summary>
/// <param name="sender">事件发送者</param>
/// <param name="e">事件参数</param>
private void OnSocketClosed(object? sender, EventArgs e)
{
try
@ -106,27 +57,8 @@ namespace CoreAgent.ProtocolClient.Managers.WebSocketMgr
}
/// <summary>
/// WebSocket消息接收事件处理器 - 对应LTEClientWebSocket.OnSocketMessage
///
/// 功能说明:
/// 1. 处理WebSocket消息接收事件
/// 2. 解析接收到的消息
/// 3. 触发MessageReceived事件
/// 4. 调用HandleReceivedMessage处理消息
///
/// 对应关系:
/// - 事件处理:对应原始实现中的OnSocketMessage方法
/// - 消息解析:对应原始实现中的消息解析逻辑
/// - 事件触发:对应原始实现中的MessageReceived事件触发
/// - 消息处理:对应原始实现中的消息处理逻辑
///
/// 重构改进:
/// - 更清晰的错误处理
/// - 更详细的日志记录
/// - 保持了完全一致的事件处理逻辑
/// WebSocket消息接收事件处理器
/// </summary>
/// <param name="sender">事件发送者</param>
/// <param name="e">消息接收事件参数</param>
private void OnSocketMessageReceived(object? sender, MessageReceivedEventArgs e)
{
try
@ -134,7 +66,7 @@ namespace CoreAgent.ProtocolClient.Managers.WebSocketMgr
var messageText = e.Message;
_logger.LogDebug($"[{_clientName}] 接收到消息: {messageText}");
// 解析消息 - 对应原始实现中的消息解析逻辑
// 解析消息
JObject? message = null;
try
{
@ -147,10 +79,10 @@ namespace CoreAgent.ProtocolClient.Managers.WebSocketMgr
return;
}
// 触发MessageReceived事件 - 对应原始实现中的事件触发
// 触发MessageReceived事件
MessageReceived?.Invoke(this, message);
// 处理消息 - 对应原始实现中的消息处理逻辑
// 处理消息
HandleReceivedMessage(message, error => ConnectionError?.Invoke(this, error));
}
catch (Exception ex)
@ -161,25 +93,8 @@ namespace CoreAgent.ProtocolClient.Managers.WebSocketMgr
}
/// <summary>
/// WebSocket错误事件处理器 - 对应LTEClientWebSocket.OnSocketError
///
/// 功能说明:
/// 1. 处理WebSocket连接错误事件
/// 2. 记录错误日志
/// 3. 触发ConnectionError事件
///
/// 对应关系:
/// - 事件处理:对应原始实现中的OnSocketError方法
/// - 错误记录:对应原始实现中的错误日志记录
/// - 事件触发:对应原始实现中的ConnectionError事件触发
///
/// 重构改进:
/// - 更详细的错误信息记录
/// - 更好的异常处理
/// - 保持了完全一致的事件处理逻辑
/// WebSocket错误事件处理器
/// </summary>
/// <param name="sender">事件发送者</param>
/// <param name="e">错误事件参数</param>
private void OnSocketError(object? sender, SuperSocket.ClientEngine.ErrorEventArgs e)
{
try
@ -195,23 +110,7 @@ namespace CoreAgent.ProtocolClient.Managers.WebSocketMgr
}
/// <summary>
/// 启动消息延迟发送定时器 - 对应LTEClientWebSocket.StartMessageDeferTimer
///
/// 功能说明:
/// 1. 启动消息延迟发送定时器,实现批量发送优化
/// 2. 当队列中消息少于100条时,延迟1毫秒发送
/// 3. 当队列中消息达到100条时,立即发送
///
/// 对应关系:
/// - 定时器创建:对应原始实现中的定时器创建逻辑
/// - 延迟策略:1毫秒延迟,与原始实现完全一致
/// - 批处理大小:100条消息,与原始实现完全一致
/// - 回调函数:对应原始实现中的定时器回调逻辑
///
/// 重构改进:
/// - 更清晰的定时器管理
/// - 更好的异常处理
/// - 保持了完全一致的批处理策略
/// 启动消息延迟发送定时器
/// </summary>
private void StartMessageDeferTimer()
{
@ -230,22 +129,7 @@ namespace CoreAgent.ProtocolClient.Managers.WebSocketMgr
}
/// <summary>
/// 停止消息延迟发送定时器 - 对应LTEClientWebSocket.StopMessageDeferTimer
///
/// 功能说明:
/// 1. 停止消息延迟发送定时器
/// 2. 释放定时器资源
/// 3. 确保线程安全的定时器管理
///
/// 对应关系:
/// - 定时器停止:对应原始实现中的定时器停止逻辑
/// - 资源释放:对应原始实现中的定时器释放逻辑
/// - 线程安全:使用锁确保线程安全
///
/// 重构改进:
/// - 更清晰的资源管理
/// - 更好的线程安全保证
/// - 保持了完全一致的停止逻辑
/// 停止消息延迟发送定时器
/// </summary>
private void StopMessageDeferTimer()
{
@ -253,35 +137,18 @@ namespace CoreAgent.ProtocolClient.Managers.WebSocketMgr
}
/// <summary>
/// 消息延迟发送定时器回调 - 对应LTEClientWebSocket.OnMessageDeferTimer
///
/// 功能说明:
/// 1. 处理消息延迟发送定时器回调
/// 2. 批量发送队列中的消息
/// 3. 实现消息发送优化
///
/// 对应关系:
/// - 定时器回调:对应原始实现中的OnMessageDeferTimer方法
/// - 批量发送:对应原始实现中的批量发送逻辑
/// - 批处理大小:100条消息,与原始实现完全一致
/// - 发送逻辑:对应原始实现中的SendMessageNow调用
///
/// 重构改进:
/// - 更清晰的批量发送逻辑
/// - 更好的异常处理
/// - 保持了完全一致的批处理策略
/// 消息延迟发送定时器回调
/// </summary>
/// <param name="state">定时器状态参数</param>
private void OnMessageDeferTimer(object? state)
{
try
{
// 批量发送消息 - 对应原始实现中的批量发送逻辑
// 批量发送消息
var messages = new List<JObject>();
var count = 0;
const int batchSize = 100; // 与原始实现完全一致
const int batchSize = 100;
// 从队列中取出消息 - 对应原始实现中的队列处理逻辑
// 从队列中取出消息
while (count < batchSize && _messageFifo.TryTake(out var message))
{
messages.Add(message);
@ -290,11 +157,11 @@ namespace CoreAgent.ProtocolClient.Managers.WebSocketMgr
if (messages.Count > 0)
{
// 发送消息 - 对应原始实现中的SendMessageNow调用
// 发送消息
SendMessageNow(messages);
}
// 如果队列中还有消息,继续启动定时器 - 对应原始实现中的定时器重启逻辑
// 如果队列中还有消息,继续启动定时器
if (_messageFifo.Count > 0)
{
StartMessageDeferTimer();
@ -307,26 +174,8 @@ namespace CoreAgent.ProtocolClient.Managers.WebSocketMgr
}
/// <summary>
/// 立即发送消息 - 对应LTEClientWebSocket.SendMessageNow
///
/// 功能说明:
/// 1. 立即发送消息列表到WebSocket
/// 2. 处理发送异常和错误
/// 3. 触发MessageSent事件
///
/// 对应关系:
/// - 消息发送:对应原始实现中的SendMessageNow方法
/// - 异常处理:对应原始实现中的发送异常处理
/// - 事件触发:对应原始实现中的事件触发逻辑
/// - 日志记录:对应原始实现中的发送日志记录
///
/// 重构改进:
/// - 更清晰的发送逻辑
/// - 更详细的错误处理
/// - 新增MessageSent事件触发
/// - 保持了完全一致的发送逻辑
/// 立即发送消息
/// </summary>
/// <param name="messages">要发送的消息列表</param>
private void SendMessageNow(List<JObject> messages)
{
if (messages == null || messages.Count == 0)
@ -345,7 +194,7 @@ namespace CoreAgent.ProtocolClient.Managers.WebSocketMgr
var messageText = JsonConvert.SerializeObject(message);
_webSocket?.Send(messageText);
// 触发MessageSent事件 - 新增功能,提供更完整的消息生命周期通知
// 触发MessageSent事件
MessageSent?.Invoke(this, message);
_logger.LogDebug($"[{_clientName}] 消息已发送: {messageText}");
@ -359,21 +208,7 @@ namespace CoreAgent.ProtocolClient.Managers.WebSocketMgr
}
/// <summary>
/// 清空消息队列 - 对应LTEClientWebSocket中的队列清理逻辑
///
/// 功能说明:
/// 1. 清空消息队列中的所有消息
/// 2. 在断开连接时调用,确保资源清理
///
/// 对应关系:
/// - 队列清理:对应原始实现中的队列清理逻辑
/// - 调用时机:在Disconnect()方法中调用
/// - 日志记录:对应原始实现中的清理日志记录
///
/// 重构改进:
/// - 更清晰的清理逻辑
/// - 更详细的日志记录
/// - 保持了完全一致的清理逻辑
/// 清空消息队列
/// </summary>
private void ClearMessageQueue()
{

283
CoreAgent.ProtocolClient/Managers/WebSocketMgr/PublicMethods.cs

@ -17,37 +17,8 @@ namespace CoreAgent.ProtocolClient.Managers.WebSocketMgr
#region 公共方法
/// <summary>
/// 连接到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服务器
/// </summary>
/// <param name="url">WebSocket URL,对应LTEClientWebSocket._config.Address</param>
/// <param name="ssl">是否使用SSL,对应LTEClientWebSocket._config.Ssl</param>
public void Connect(string url, bool ssl = false)
{
ThrowIfDisposed();
@ -59,20 +30,20 @@ namespace CoreAgent.ProtocolClient.Managers.WebSocketMgr
{
_logger.LogInformation($"[{_clientName}] 尝试连接: {url}");
// 构建WebSocket URL - 对应原始实现中的URL构建逻辑
// 构建WebSocket URL
var fullUrl = (ssl ? "wss://" : "ws://") + url;
// 创建WebSocket实例 - 对应原始实现中的_webSocket创建
// 创建WebSocket实例
_webSocket = new WebSocket(fullUrl);
_webSocket.EnableAutoSendPing = false;
// 绑定事件处理器 - 对应原始实现中的事件绑定
// 绑定事件处理器
_webSocket.Opened += OnSocketOpened!;
_webSocket.Closed += OnSocketClosed!;
_webSocket.MessageReceived += OnSocketMessageReceived!; // 对应OnSocketMessage0
_webSocket.MessageReceived += OnSocketMessageReceived!;
_webSocket.Error += OnSocketError!;
// 打开连接 - 对应原始实现中的_webSocket.Open()
// 打开连接
_webSocket.Open();
}
catch (Exception ex)
@ -84,31 +55,7 @@ namespace CoreAgent.ProtocolClient.Managers.WebSocketMgr
}
/// <summary>
/// 断开WebSocket连接 - 对应LTEClientWebSocket.Stop()方法中的WebSocket相关逻辑
///
/// 功能说明:
/// 1. 关闭WebSocket连接,对应原始Stop()方法的核心逻辑
/// 2. 清理消息队列和定时器,对应原始的资源清理逻辑
/// 3. 提供更完善的异常处理
///
/// 与原始实现的差异:
/// - 方法名从Stop()改为Disconnect(),更明确表达功能
/// - 移除了状态管理逻辑(SetState),专注连接管理
/// - 移除了重连逻辑,专注连接断开
///
/// 详细对应关系:
/// - 定时器停止:对应原始StopTimers()中的_messageDeferTimer处理
/// - 队列清理:对应原始实现中的队列清理逻辑
/// - WebSocket关闭:对应原始实现中的_webSocket.Close()
/// - 资源清理:对应原始实现中的资源清理逻辑
/// - 异常处理:对应原始实现中的异常处理
/// - 日志记录:对应原始实现中的日志记录
///
/// 重构改进:
/// - 更清晰的资源清理顺序
/// - 更完善的异常处理
/// - 更详细的日志记录
/// - 保持了完全一致的清理逻辑
/// 断开WebSocket连接
/// </summary>
public void Disconnect()
{
@ -118,13 +65,13 @@ namespace CoreAgent.ProtocolClient.Managers.WebSocketMgr
{
_logger.LogInformation($"[{_clientName}] 断开连接");
// 停止消息发送定时器 - 对应原始StopTimers()中的_messageDeferTimer处理
// 停止消息发送定时器
StopMessageDeferTimer();
// 清空消息队列 - 对应原始实现中的队列清理
// 清空消息队列
ClearMessageQueue();
// 关闭WebSocket连接 - 对应原始实现中的_webSocket.Close()
// 关闭WebSocket连接
if (_webSocket != null)
{
_webSocket.Close();
@ -138,41 +85,8 @@ namespace CoreAgent.ProtocolClient.Managers.WebSocketMgr
}
/// <summary>
/// 发送消息 - 对应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管理
/// - 更严格的参数验证
/// - 更详细的日志记录
/// - 保持了完全一致的发送逻辑
/// 发送消息
/// </summary>
/// <param name="message">消息对象,对应原始方法中的message参数</param>
/// <param name="callback">回调函数,对应原始方法中的callback参数</param>
/// <param name="errorHandler">是否为错误处理器,对应原始方法中的errorHandler参数</param>
/// <returns>消息ID,对应原始方法的返回值</returns>
public long SendMessage(JObject message, Action<JObject>? callback = null, bool errorHandler = false)
{
ThrowIfDisposed();
@ -180,20 +94,20 @@ namespace CoreAgent.ProtocolClient.Managers.WebSocketMgr
if (message == null)
throw new ArgumentNullException(nameof(message));
// 检查连接状态 - 对应原始实现中的连接状态检查
// 检查连接状态
if (!IsConnected)
{
_logger.LogWarning($"[{_clientName}] WebSocket未连接,无法发送消息");
return -1L;
}
// 使用MessageIdManager生成ID - 替代原始的Interlocked.Increment(ref _messageId)
// 使用MessageIdManager生成ID
var messageId = _messageIdManager.GenerateGeneralMessageId(message, callback, errorHandler);
// 添加到消息队列 - 对应原始实现中的_messageFifo.Enqueue(message)
// 添加到消息队列
_messageFifo.Add(message);
// 启动消息发送定时器 - 对应原始实现中的定时器启动逻辑
// 启动消息发送定时器
StartMessageDeferTimer();
_logger.LogDebug($"[{_clientName}] 消息已加入队列: message_id={messageId}");
@ -201,36 +115,8 @@ namespace CoreAgent.ProtocolClient.Managers.WebSocketMgr
}
/// <summary>
/// 发送日志获取消息 - 对应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
/// - 保持了完全一致的发送逻辑
/// 发送日志获取消息
/// </summary>
/// <param name="message">消息对象,对应原始LogGet()方法中构建的message</param>
/// <param name="callback">回调函数,对应原始LogGet()方法中的LogGetParse回调</param>
/// <returns>消息ID,对应原始方法的返回值</returns>
public long SendLogGetMessage(JObject message, Action<JObject> callback)
{
ThrowIfDisposed();
@ -241,19 +127,17 @@ namespace CoreAgent.ProtocolClient.Managers.WebSocketMgr
if (callback == null)
throw new ArgumentNullException(nameof(callback));
// 检查连接状态 - 对应原始实现中的连接状态检查
// 检查连接状态
if (!IsConnected)
{
_logger.LogWarning($"[{_clientName}] WebSocket未连接,无法发送日志获取消息");
return -1L;
}
// 使用MessageIdManager生成LogGet ID - 替代原始的_logGetId管理
// 使用MessageIdManager生成LogGet ID
var messageId = _messageIdManager.GenerateLogGetMessageId(message, callback);
// 委托给SendMessage方法,避免代码重复 - 对应原始实现中的SendMessage调用
// 注意:这里不需要再次调用SendMessage,因为GenerateLogGetMessageId已经处理了消息ID和回调注册
// 只需要将消息加入队列并启动定时器
// 将消息加入队列并启动定时器
_messageFifo.Add(message);
StartMessageDeferTimer();
@ -262,38 +146,8 @@ namespace CoreAgent.ProtocolClient.Managers.WebSocketMgr
}
/// <summary>
/// 处理接收到的消息 - 对应LTEClientWebSocket.OnSocketMessage()方法中的消息处理逻辑
///
/// 功能说明:
/// 1. 处理接收到的WebSocket消息,对应原始OnSocketMessage()方法的核心逻辑
/// 2. 使用MessageIdManager处理消息响应,替代原始的消息处理器查找逻辑
/// 3. 触发MessageReceived事件,对应原始的事件触发
/// 4. 提供完善的错误处理
///
/// 与原始实现的差异:
/// - 使用MessageIdManager处理消息响应,提供更好的管理
/// - 移除了消息缓存逻辑(_receivedMessages),专注处理
/// - 移除了业务逻辑处理(log_get、stats等),专注消息路由
/// - 保持了完全一致的事件触发逻辑
///
/// 详细对应关系:
/// - 参数message:对应原始方法中的msg参数
/// - 参数errorHandler:对应原始方法中的错误处理逻辑
/// - 消息处理器查找:对应原始的消息处理器查找逻辑
/// - 事件触发:对应原始的事件触发逻辑
/// - 错误处理:对应原始的错误处理逻辑
/// - 返回值:新增返回值提供处理状态反馈
/// - 日志记录:对应原始实现中的日志记录
///
/// 重构改进:
/// - 更统一的消息响应处理
/// - 更清晰的错误处理
/// - 更详细的日志记录
/// - 保持了完全一致的处理逻辑
/// 处理接收到的消息
/// </summary>
/// <param name="message">接收到的消息,对应原始方法中的msg参数</param>
/// <param name="errorHandler">错误处理回调,对应原始方法中的错误处理逻辑</param>
/// <returns>是否成功处理,新增返回值提供处理状态反馈</returns>
public bool HandleReceivedMessage(JObject message, Action<string>? errorHandler = null)
{
ThrowIfDisposed();
@ -303,7 +157,7 @@ namespace CoreAgent.ProtocolClient.Managers.WebSocketMgr
try
{
// 使用MessageIdManager处理消息响应 - 替代原始的消息处理器查找逻辑
// 使用MessageIdManager处理消息响应
var handled = _messageIdManager.HandleMessageResponse(message, errorHandler);
if (handled)
@ -312,8 +166,7 @@ namespace CoreAgent.ProtocolClient.Managers.WebSocketMgr
return true;
}
// 处理特定消息类型 - 对应原始实现中的特定消息类型处理
// 注意:这里不处理log_get和stats等业务逻辑,因为重构版本专注于消息传输
// 处理特定消息类型
var name = message["message"]?.ToString();
if (!string.IsNullOrEmpty(name))
{
@ -331,26 +184,8 @@ namespace CoreAgent.ProtocolClient.Managers.WebSocketMgr
}
/// <summary>
/// 设置消息处理器 - 对应LTEClientWebSocket.SetMessageHandler()方法
///
/// 功能说明:
/// 1. 设置按名称的消息处理器,对应原始SetMessageHandler()方法的核心逻辑
/// 2. 委托给MessageIdManager处理,提供统一的消息处理器管理
/// 3. 支持多个消息名称的处理器设置
///
/// 详细对应关系:
/// - 参数names:对应原始方法中的names参数,消息名称数组
/// - 参数handler:对应原始方法中的handler参数,消息处理器
/// - 处理器注册:对应原始的_messageHandlersByName注册逻辑
/// - 日志记录:对应原始实现中的日志记录
///
/// 重构改进:
/// - 委托给MessageIdManager,提供统一管理
/// - 保持了完全一致的接口和功能
/// - 更好的错误处理和参数验证
/// 设置消息处理器
/// </summary>
/// <param name="names">消息名称数组,对应原始方法中的names参数</param>
/// <param name="handler">消息处理器,对应原始方法中的handler参数</param>
public void SetMessageHandler(string[] names, MessageHandler handler)
{
ThrowIfDisposed();
@ -358,24 +193,8 @@ namespace CoreAgent.ProtocolClient.Managers.WebSocketMgr
}
/// <summary>
/// 取消设置消息处理器 - 对应LTEClientWebSocket.UnsetMessageHandler()方法
///
/// 功能说明:
/// 1. 取消按名称的消息处理器,对应原始UnsetMessageHandler()方法的核心逻辑
/// 2. 委托给MessageIdManager处理,提供统一的消息处理器管理
/// 3. 支持多个消息名称的处理器取消
///
/// 详细对应关系:
/// - 参数names:对应原始方法中的names参数,消息名称数组
/// - 处理器移除:对应原始的_messageHandlersByName移除逻辑
/// - 日志记录:对应原始实现中的日志记录
///
/// 重构改进:
/// - 委托给MessageIdManager,提供统一管理
/// - 保持了完全一致的接口和功能
/// - 更好的错误处理和参数验证
/// 取消设置消息处理器
/// </summary>
/// <param name="names">消息名称数组,对应原始方法中的names参数</param>
public void UnsetMessageHandler(string[] names)
{
ThrowIfDisposed();
@ -383,25 +202,8 @@ namespace CoreAgent.ProtocolClient.Managers.WebSocketMgr
}
/// <summary>
/// 检查是否为当前日志获取消息 - 对应LTEClientWebSocket中的_logGetId检查逻辑
///
/// 功能说明:
/// 1. 检查指定的消息ID是否为当前的日志获取消息ID
/// 2. 委托给MessageIdManager处理,提供统一的LogGet ID管理
/// 3. 用于日志获取流程的状态检查
///
/// 详细对应关系:
/// - 参数messageId:对应原始实现中的消息ID检查
/// - 返回值:true表示是当前LogGet消息,false表示不是,对应原始逻辑
/// - 检查逻辑:对应原始的_logGetId比较逻辑
///
/// 重构改进:
/// - 委托给MessageIdManager,提供统一管理
/// - 保持了完全一致的检查逻辑
/// - 更好的线程安全性
/// 检查是否为当前日志获取消息
/// </summary>
/// <param name="messageId">要检查的消息ID</param>
/// <returns>是否为当前日志获取消息</returns>
public bool IsCurrentLogGetMessage(long messageId)
{
ThrowIfDisposed();
@ -409,22 +211,7 @@ namespace CoreAgent.ProtocolClient.Managers.WebSocketMgr
}
/// <summary>
/// 重置日志获取ID - 对应LTEClientWebSocket中的_logGetId重置逻辑
///
/// 功能说明:
/// 1. 重置日志获取消息ID,对应原始实现中的_logGetId重置逻辑
/// 2. 委托给MessageIdManager处理,提供统一的LogGet ID管理
/// 3. 用于日志获取流程的重置操作
///
/// 详细对应关系:
/// - 重置逻辑:对应原始的_logGetId = -1操作
/// - 日志记录:对应原始实现中的日志记录
/// - 事件触发:对应原始实现中的状态变化通知
///
/// 重构改进:
/// - 委托给MessageIdManager,提供统一管理
/// - 保持了完全一致的重置逻辑
/// - 更好的事件通知机制
/// 重置日志获取ID
/// </summary>
public void ResetLogGetId()
{
@ -433,24 +220,8 @@ namespace CoreAgent.ProtocolClient.Managers.WebSocketMgr
}
/// <summary>
/// 清理过期的消息处理器 - 对应LTEClientWebSocket中的处理器清理逻辑
///
/// 功能说明:
/// 1. 清理过期的消息处理器,防止内存泄漏
/// 2. 委托给MessageIdManager处理,提供统一的处理器管理
/// 3. 支持可配置的过期时间
///
/// 详细对应关系:
/// - 参数maxAge:对应原始实现中的过期时间配置
/// - 清理逻辑:对应原始的处理器清理逻辑
/// - 日志记录:对应原始实现中的日志记录
///
/// 重构改进:
/// - 委托给MessageIdManager,提供统一管理
/// - 保持了完全一致的清理逻辑
/// - 更好的内存管理
/// 清理过期的消息处理器
/// </summary>
/// <param name="maxAge">最大存活时间(毫秒),默认30000毫秒</param>
public void CleanupExpiredHandlers(int maxAge = 30000)
{
ThrowIfDisposed();

408
CoreAgent.ProtocolClient/Managers/WebSocketMgr/WebSocketMessageManager.cs

@ -12,331 +12,53 @@ using WebSocket4Net;
namespace CoreAgent.ProtocolClient.Managers.WebSocketMgr
{
/// <summary>
/// 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提供更好的消息管理
/// WebSocket消息管理器 - 处理WebSocket连接和消息传输
/// </summary>
public partial class WebSocketMessageManager : IDisposable
{
#region 私有字段
/// <summary>
/// WebSocket实例 - 对应LTEClientWebSocket._webSocket
/// 负责底层的WebSocket连接和通信
///
/// 对应关系:
/// - 创建:Connect()方法中创建,对应Start()方法中的_webSocket = new WebSocket(url)
/// - 配置:EnableAutoSendPing = false,对应原始实现
/// - 事件绑定:绑定Opened/Closed/MessageReceived/Error事件,对应原始事件绑定
/// - 连接:调用Open()方法,对应原始的_webSocket.Open()
/// - 关闭:调用Close()方法,对应原始的_webSocket.Close()
/// - 清理:在Disconnect()中设置为null,对应原始的资源清理
/// </summary>
// WebSocket连接实例
private WebSocket? _webSocket;
/// <summary>
/// 消息ID管理器 - 对应LTEClientWebSocket._messageId和_logGetId
/// 统一管理通用消息ID和日志获取消息ID,提供更好的消息跟踪和回调管理
///
/// 对应关系:
/// - _messageId 对应 MessageIdManager.GenerateGeneralMessageId()
/// - _logGetId 对应 MessageIdManager.GenerateLogGetMessageId()
/// - _messageHandlers 对应 MessageIdManager的内部处理器管理
/// - _messageHandlersByName 对应 MessageIdManager的按名称处理器管理
///
/// 功能增强:
/// - 线程安全的ID生成,替代原始的Interlocked.Increment
/// - 统一的处理器管理,提供更好的回调跟踪
/// - 自动清理过期处理器,防止内存泄漏
/// - 事件通知机制,提供ID变化通知
/// </summary>
private readonly MessageIdManager _messageIdManager;
// 客户端名称
private readonly string _clientName;
/// <summary>
/// 日志记录器 - 对应LTEClientWebSocket._logger
/// 用于记录WebSocket操作和错误信息
///
/// 对应关系:
/// - 构造函数参数:对应LTEClientWebSocket构造函数中的logger参数
/// - 日志记录:对应原始实现中的所有_logger.LogXXX调用
/// - 日志格式:保持与原始实现一致的日志格式
///
/// 功能增强:
/// - 更详细的错误日志记录
/// - 更好的异常堆栈跟踪
/// - 统一的日志格式和级别
/// </summary>
private readonly ILogger _logger;
// 日志记录器
private readonly ILogger<WebSocketMessageManager> _logger;
/// <summary>
/// 客户端名称 - 对应LTEClientWebSocket._config.Name
/// 用于日志记录和事件标识
///
/// 对应关系:
/// - 构造函数参数:对应LTEClientWebSocket构造函数中的config.Name
/// - 日志前缀:对应原始实现中所有日志的[{_config.Name}]前缀
/// - 事件标识:用于区分不同客户端的事件
///
/// 功能增强:
/// - 参数验证:确保clientName不为null
/// - 统一标识:在所有日志和事件中使用一致的客户端标识
/// </summary>
private readonly string _clientName;
// 消息ID管理器
private readonly MessageIdManager _messageIdManager;
/// <summary>
/// 消息队列 - 对应LTEClientWebSocket._messageFifo
/// 线程安全的阻塞集合,用于批量发送优化
/// 优化说明:从ConcurrentQueue改为BlockingCollection,提供更好的线程安全性和阻塞能力
///
/// 对应关系:
/// - 队列类型:BlockingCollection<JObject>,优化后的线程安全集合
/// - 入队操作:Add(message),对应原始的_messageFifo.Enqueue(message)
/// - 出队操作:TryTake(out message),对应原始的队列处理逻辑
/// - 批量处理:支持批量消息发送,对应原始的批处理逻辑
///
/// 功能增强:
/// - 线程安全:使用BlockingCollection保证线程安全
/// - 阻塞能力:支持阻塞式出队操作,提高性能
/// - 批量优化:支持批量发送减少网络开销
/// - 延迟发送:配合_messageDeferTimer实现延迟发送
/// - 资源管理:自动处理集合的完成状态
///
/// 重构改进:
/// - 移除了消息缓存功能,专注传输
/// - 优化了队列操作逻辑,提供更好的性能
/// - 增强了线程安全性和资源管理
/// </summary>
// 消息队列
private readonly BlockingCollection<JObject> _messageFifo;
/// <summary>
/// 消息延迟发送定时器 - 对应LTEClientWebSocket._messageDeferTimer
/// 用于实现消息的批量发送和延迟发送机制
/// 保持与原始实现完全一致的逻辑
///
/// 对应关系:
/// - 定时器类型:Timer,与原始实现完全一致
/// - 启动逻辑:StartMessageDeferTimer(),对应原始的定时器启动
/// - 停止逻辑:StopMessageDeferTimer(),对应原始的定时器停止
/// - 延迟策略:1毫秒延迟,与原始实现完全一致
/// - 批处理大小:100条消息,与原始实现完全一致
///
/// 功能保持:
/// - 批量发送:当队列中消息少于100条时,延迟1毫秒发送
/// - 立即发送:当队列中消息达到100条时,立即发送
/// - 资源管理:在Disconnect()和Dispose()中正确释放
///
/// 重构改进:
/// - 更清晰的定时器管理逻辑
/// - 更好的异常处理
/// - 保持了完全一致的批处理策略
/// </summary>
// 消息延迟发送定时器
private Timer? _messageDeferTimer;
/// <summary>
/// 释放标志 - 对应LTEClientWebSocket._disposed
/// 防止重复释放和已释放对象的操作
///
/// 对应关系:
/// - 字段类型:bool,对应原始的_disposed字段
/// - 返回值:true表示已释放,false表示未释放,对应原始实现
/// - 使用场景:外部检查对象释放状态,对应原始实现
/// - 线程安全:直接返回_disposed字段值,对应原始实现
///
/// 功能保持:
/// - 释放状态检查:外部可以检查对象是否已释放
/// - 资源保护:防止对已释放对象的操作
/// - 状态查询:提供对象状态的查询接口
///
/// 重构改进:
/// - 保持了完全一致的检查逻辑
/// - 保持了完全一致的返回值语义
/// - 保持了完全一致的使用场景
/// </summary>
// 释放标志
private bool _disposed;
/// <summary>
/// 同步锁对象
/// 用于确保线程安全的操作
///
/// 对应关系:
/// - 新增功能:原始实现中没有显式的同步锁
/// - 用途:确保关键操作的线程安全
/// - 使用场景:在需要线程安全的地方使用lock语句
///
/// 功能增强:
/// - 线程安全:确保关键操作的原子性
/// - 死锁预防:使用细粒度锁避免死锁
/// - 性能优化:最小化锁的持有时间
/// </summary>
// 同步锁对象
private readonly object _lockObject = new object();
#endregion
#region 事件
/// <summary>
/// 连接打开事件 - 对应LTEClientWebSocket.ConnectionOpened
/// 当WebSocket连接成功建立时触发
///
/// 对应关系:
/// - 事件类型:EventHandler,与原始实现完全一致
/// - 触发时机:在OnSocketOpened()中触发,对应原始的OnSocketOpened事件处理
/// - 触发条件:WebSocket连接成功建立时
/// - 事件参数:无参数,与原始实现完全一致
///
/// 功能保持:
/// - 连接状态通知:通知外部连接已建立
/// - 事件订阅:支持多个订阅者
/// - 异步触发:事件触发不会阻塞WebSocket操作
///
/// 重构改进:
/// - 更清晰的触发时机
/// - 更好的错误处理
/// - 保持了完全一致的事件接口
/// </summary>
// 连接打开事件
public event EventHandler? ConnectionOpened;
/// <summary>
/// 连接关闭事件 - 对应LTEClientWebSocket.ConnectionClosed
/// 当WebSocket连接关闭时触发
///
/// 对应关系:
/// - 事件类型:EventHandler,与原始实现完全一致
/// - 触发时机:在OnSocketClosed()中触发,对应原始的OnSocketClosed事件处理
/// - 触发条件:WebSocket连接关闭时
/// - 事件参数:无参数,与原始实现完全一致
///
/// 功能保持:
/// - 连接状态通知:通知外部连接已关闭
/// - 事件订阅:支持多个订阅者
/// - 异步触发:事件触发不会阻塞WebSocket操作
///
/// 重构改进:
/// - 更清晰的触发时机
/// - 更好的资源清理
/// - 保持了完全一致的事件接口
/// </summary>
// 连接关闭事件
public event EventHandler? ConnectionClosed;
/// <summary>
/// 连接错误事件 - 对应LTEClientWebSocket.ConnectionError
/// 当WebSocket连接发生错误时触发
///
/// 对应关系:
/// - 事件类型:EventHandler<string>,与原始实现完全一致
/// - 触发时机:在OnSocketError()和异常处理中触发,对应原始的错误处理
/// - 触发条件:WebSocket连接错误、消息处理错误等
/// - 事件参数:错误信息字符串,与原始实现完全一致
///
/// 功能保持:
/// - 错误通知:通知外部连接或处理错误
/// - 事件订阅:支持多个订阅者
/// - 异步触发:事件触发不会阻塞WebSocket操作
///
/// 重构改进:
/// - 更详细的错误信息
/// - 更好的异常处理
/// - 保持了完全一致的事件接口
/// </summary>
// 连接错误事件
public event EventHandler<string>? ConnectionError;
/// <summary>
/// 消息接收事件 - 对应LTEClientWebSocket.MessageReceived
/// 当接收到WebSocket消息时触发
///
/// 对应关系:
/// - 事件类型:EventHandler<JObject>,与原始实现完全一致
/// - 触发时机:在OnSocketMessageReceived()中触发,对应原始的OnSocketMessage事件处理
/// - 触发条件:接收到WebSocket消息并解析成功后
/// - 事件参数:解析后的JObject消息,与原始实现完全一致
///
/// 功能保持:
/// - 消息通知:通知外部接收到新消息
/// - 事件订阅:支持多个订阅者
/// - 异步触发:事件触发不会阻塞消息处理
/// - 触发顺序:在消息处理开始时就触发,与原始实现完全一致
///
/// 重构改进:
/// - 更清晰的触发时机
/// - 更好的消息解析
/// - 保持了完全一致的事件接口和触发顺序
/// </summary>
// 消息接收事件
public event EventHandler<JObject>? MessageReceived;
/// <summary>
/// 消息发送事件 - 新增功能,LTEClientWebSocket中没有对应事件
/// 当消息成功发送时触发,提供更完整的消息生命周期通知
///
/// 对应关系:
/// - 事件类型:EventHandler<JObject>,与MessageReceived保持一致
/// - 触发时机:在SendMessageNow()中触发,对应消息发送成功时
/// - 触发条件:消息成功发送到WebSocket时
/// - 事件参数:发送的JObject消息,与MessageReceived保持一致
///
/// 功能增强:
/// - 消息生命周期:提供完整的消息发送通知
/// - 调试支持:便于调试消息发送流程
/// - 监控支持:便于监控消息发送状态
/// - 事件订阅:支持多个订阅者
///
/// 重构优势:
/// - 更完整的消息生命周期管理
/// - 更好的调试和监控支持
/// - 与MessageReceived事件形成对称的事件系统
/// </summary>
// 消息发送事件
public event EventHandler<JObject>? MessageSent;
#endregion
@ -344,113 +66,27 @@ namespace CoreAgent.ProtocolClient.Managers.WebSocketMgr
#region 属性
/// <summary>
/// 是否已连接 - 对应LTEClientWebSocket.IsConnected
/// 检查WebSocket连接状态
///
/// 对应关系:
/// - 属性类型:bool,与原始实现完全一致
/// - 检查逻辑:_webSocket?.State == WebSocketState.Open,与原始实现完全一致
/// - 使用场景:在SendMessage()中检查连接状态,对应原始实现
/// - 返回值:true表示已连接,false表示未连接,与原始实现完全一致
///
/// 功能保持:
/// - 连接状态检查:快速检查WebSocket连接状态
/// - 空安全:使用?.操作符避免空引用异常
/// - 实时状态:反映WebSocket的实时连接状态
///
/// 重构改进:
/// - 保持了完全一致的检查逻辑
/// - 保持了完全一致的返回值语义
/// - 保持了完全一致的使用场景
/// 是否已连接
/// </summary>
public bool IsConnected => _webSocket?.State == WebSocketState.Open;
/// <summary>
/// WebSocket状态 - 对应LTEClientWebSocket._webSocket?.State
/// 获取详细的WebSocket连接状态
///
/// 对应关系:
/// - 属性类型:WebSocketState,对应原始的_webSocket?.State
/// - 返回值:WebSocket的详细状态,对应原始实现
/// - 空安全:使用??操作符提供默认值,对应原始实现
/// - 使用场景:提供更详细的连接状态信息
///
/// 功能保持:
/// - 详细状态:提供WebSocket的详细连接状态
/// - 空安全:当_webSocket为null时返回WebSocketState.None
/// - 实时状态:反映WebSocket的实时状态
///
/// 重构改进:
/// - 保持了完全一致的状态获取逻辑
/// - 保持了完全一致的默认值处理
/// - 保持了完全一致的使用场景
/// WebSocket状态
/// </summary>
public WebSocketState State => _webSocket?.State ?? WebSocketState.None;
/// <summary>
/// 是否已释放 - 对应LTEClientWebSocket._disposed
/// 检查对象是否已被释放
///
/// 对应关系:
/// - 属性类型:bool,对应原始的_disposed字段
/// - 返回值:true表示已释放,false表示未释放,对应原始实现
/// - 使用场景:外部检查对象释放状态,对应原始实现
/// - 线程安全:直接返回_disposed字段值,对应原始实现
///
/// 功能保持:
/// - 释放状态检查:外部可以检查对象是否已释放
/// - 资源保护:防止对已释放对象的操作
/// - 状态查询:提供对象状态的查询接口
///
/// 重构改进:
/// - 保持了完全一致的检查逻辑
/// - 保持了完全一致的返回值语义
/// - 保持了完全一致的使用场景
/// 是否已释放
/// </summary>
public bool IsDisposed => _disposed;
/// <summary>
/// 消息队列数量 - 对应LTEClientWebSocket._messageFifo.Count
/// 获取当前待发送消息的数量
///
/// 对应关系:
/// - 属性类型:int,对应原始的_messageFifo.Count
/// - 返回值:队列中待发送消息的数量,对应原始实现
/// - 使用场景:监控消息队列状态,对应原始实现
/// - 线程安全:BlockingCollection.Count是线程安全的,对应原始实现
///
/// 功能保持:
/// - 队列监控:监控当前待发送消息的数量
/// - 性能监控:便于监控消息发送性能
/// - 调试支持:便于调试消息队列状态
///
/// 重构改进:
/// - 保持了完全一致的计数逻辑
/// - 保持了完全一致的返回值语义
/// - 保持了完全一致的使用场景
/// - 优化:使用BlockingCollection提供更好的线程安全性
/// 消息队列数量
/// </summary>
public int MessageQueueCount => _messageFifo.Count;
/// <summary>
/// 消息ID管理器 - 提供对MessageIdManager的访问
/// 允许外部访问消息ID管理功能
///
/// 对应关系:
/// - 属性类型:MessageIdManager,对应原始的_messageId和_logGetId管理
/// - 返回值:内部的消息ID管理器实例,对应原始实现
/// - 使用场景:外部访问消息ID管理功能,对应原始实现
/// - 封装性:提供对内部MessageIdManager的访问,对应原始实现
///
/// 功能保持:
/// - 功能访问:外部可以访问消息ID管理功能
/// - 封装性:保持内部实现的封装性
/// - 扩展性:支持外部扩展消息ID管理功能
///
/// 重构改进:
/// - 更统一的消息ID管理接口
/// - 更好的功能封装
/// - 保持了完全一致的功能访问方式
/// 消息ID管理器
/// </summary>
public MessageIdManager MessageIdManager => _messageIdManager;

25
CoreAgent.ProtocolClient/Models/ProtocolLog.cs → CoreAgent.ProtocolClient/Models/BuildProtocolLog.cs

@ -7,7 +7,10 @@ using System.Threading.Tasks;
namespace CoreAgent.ProtocolClient.Models
{
public class ProtocolLog
/// <summary>
/// 构建协议日志模型
/// </summary>
public class BuildProtocolLog
{
#region 基础属性
/// <summary>
@ -65,20 +68,20 @@ namespace CoreAgent.ProtocolClient.Models
#endregion
#region PHY层相关属性
public PhyFields Phy { get; set; } = new();
public PhyLayerFields Phy { get; set; } = new();
#endregion
#region 数据相关属性
public DataFields DataInfo { get; set; } = new();
public DataLayerFields DataInfo { get; set; } = new();
#endregion
#region MAC层相关属性
public MacFields Mac { get; set; } = new();
public MacLayerFields Mac { get; set; } = new();
#endregion
}
// PHY层分组
public class PhyFields
public class PhyLayerFields
{
/// <summary>
/// 物理信道类型
@ -135,7 +138,7 @@ namespace CoreAgent.ProtocolClient.Models
}
// 数据相关分组
public class DataFields
public class DataLayerFields
{
/// <summary>
/// IP长度
@ -156,7 +159,7 @@ namespace CoreAgent.ProtocolClient.Models
}
// MAC层分组
public class MacFields
public class MacLayerFields
{
/// <summary>
/// 功率余量报告(PHR)
@ -194,17 +197,17 @@ namespace CoreAgent.ProtocolClient.Models
/// <summary>
/// ProtocolLog扩展方法类
/// 提供ProtocolLog相关的工具方法和扩展功能
/// BuildProtocolLog扩展方法类
/// 提供BuildProtocolLog相关的工具方法和扩展功能
/// </summary>
public static class ProtocolLogExtensions
public static class BuildProtocolLogExtensions
{
/// <summary>
/// 获取协议日志数据的字符串表示
/// </summary>
/// <param name="log">协议日志对象</param>
/// <returns>格式化的日志数据字符串</returns>
public static string GetProtocolLogData(this ProtocolLog log)
public static string GetProtocolLogData(this BuildProtocolLog log)
{
return string.Join("\n", log.Data);
}

26
CoreAgent.ProtocolClient/Models/ProtocolLogJson.cs → CoreAgent.ProtocolClient/Models/SourceProtocolLog.cs

@ -8,7 +8,10 @@ using System.Threading.Tasks;
namespace CoreAgent.ProtocolClient.Models
{
public record class ProtocolLogJson
/// <summary>
/// 源协议日志模型
/// </summary>
public record class SourceProtocolLog
{
/// <summary>
/// 消息ID
@ -41,17 +44,9 @@ namespace CoreAgent.ProtocolClient.Models
public JToken? Logs { get; set; }
/// <summary>
/// 初始化协议日志实体类的新实例
/// 初始化协议日志的新实例
/// </summary>
/// <param name="message">消息内容</param>
/// <param name="type">消息类型</param>
/// <param name="version">协议版本</param>
/// <param name="time">时间戳</param>
/// <param name="utc">UTC时间戳</param>
/// <param name="logs">日志明细</param>
/// <param name="messageId">消息ID</param>
/// <param name="headers">消息头信息</param>
public ProtocolLogJson(
public SourceProtocolLog(
string message,
string? type,
string? version,
@ -71,9 +66,9 @@ namespace CoreAgent.ProtocolClient.Models
/// <summary>
/// 协议日志明细
/// 协议日志明细模型
/// </summary>
public class ProtocolLogDetailJson
public class SourceProtocolLogDetail
{
/// <summary>
/// 源信息
@ -153,15 +148,14 @@ namespace CoreAgent.ProtocolClient.Models
[JsonProperty("rnti")]
public int? Rnti { get; set; }
/// <summary>
/// 深拷贝当前对象
/// </summary>
public ProtocolLogDetailJson DeepClone()
public SourceProtocolLogDetail DeepClone()
{
// 通过序列化和反序列化实现深拷贝
var json = JsonConvert.SerializeObject(this);
return JsonConvert.DeserializeObject<ProtocolLogDetailJson>(json)
return JsonConvert.DeserializeObject<SourceProtocolLogDetail>(json)
?? throw new InvalidOperationException("深拷贝失败,反序列化结果为 null");
}
}

14
CoreAgent.ProtocolClient/Models/TmsiMatchProcessor.cs

@ -64,7 +64,7 @@ namespace CoreAgent.ProtocolClient.Models
{
// 从UE链中获取IMSI
string imsi = GetImsiFromUeChain(receiveUeId, ueChains);
// 创建匹配结果
var matchResult = new TmsiMatchResult(tmsi, requestUeId, receiveUeId, imsi);
matches.Add(matchResult);
@ -85,7 +85,7 @@ namespace CoreAgent.ProtocolClient.Models
var processedUeIds = new HashSet<int>();
// 遍历所有TMSI匹配关系,构建链式结构
foreach (var requestKvp in _tmsiToUeId)
foreach (var requestKvp in _tmsiToUeId)
{
uint tmsi = requestKvp.Key;
int requestUeId = requestKvp.Value;
@ -101,7 +101,7 @@ namespace CoreAgent.ProtocolClient.Models
// 使用链的根节点(最外层节点)作为键
int rootUeId = chain[0];
ueChains[rootUeId] = chain;
// 标记所有UE ID为已处理
foreach (int ueId in chain)
{
@ -205,7 +205,7 @@ namespace CoreAgent.ProtocolClient.Models
return imsiMapping.Key;
}
}
// 2. 如果当前UE ID没有IMSI,查找整个UE链中的IMSI
// 遍历所有IMSI映射,查找是否有任何UE ID对应的IMSI
foreach (var imsiMapping in _imsiToUeId)
@ -215,7 +215,7 @@ namespace CoreAgent.ProtocolClient.Models
return imsiMapping.Key;
}
}
return string.Empty;
}
@ -226,7 +226,7 @@ namespace CoreAgent.ProtocolClient.Models
public UeChainStats GetUeChainStats()
{
var ueChains = BuildUeChains();
return new UeChainStats
{
TotalChains = ueChains.Count,
@ -321,4 +321,4 @@ namespace CoreAgent.ProtocolClient.Models
/// </summary>
public string Imsi { get; set; } = string.Empty;
}
}
}

63
CoreAgent.ProtocolClient/Models/TmsiMatchResult.cs

@ -53,4 +53,65 @@ namespace CoreAgent.ProtocolClient.Models
return $"TMSI: 0x{Tmsi:X8}, RequestUE: {RequestUeId}, ReceiveUE: {ReceiveUeId}{imsiInfo}";
}
}
}
/// <summary>
/// Service Request TMSI映射实体
/// 用于平铺 SrTmsiToUeId 字典数据,包含 IMSI、TMSI、UE ID、Root UE ID 四个字段
/// </summary>
public class SrTmsiMapping
{
/// <summary>
/// IMSI标识符
/// </summary>
public string Imsi { get; set; } = string.Empty;
/// <summary>
/// Service Request TMSI标识符
/// </summary>
public uint Tmsi { get; set; }
/// <summary>
/// UE ID
/// </summary>
public int UeId { get; set; }
/// <summary>
/// Root UE ID(链的根节点)
/// </summary>
public int RootUeId { get; set; }
/// <summary>
/// PLMN标识符
/// </summary>
public string Plmn { get; set; } = string.Empty;
/// <summary>
/// 构造函数
/// </summary>
/// <param name="imsi">IMSI标识符</param>
/// <param name="tmsi">Service Request TMSI标识符</param>
/// <param name="ueId">UE ID</param>
/// <param name="rootUeId">Root UE ID</param>
/// <param name="plmn">PLMN标识符</param>
public SrTmsiMapping(string imsi, uint tmsi, int ueId, int rootUeId = 0, string plmn = "")
{
Imsi = imsi;
Tmsi = tmsi;
UeId = ueId;
RootUeId = rootUeId;
Plmn = plmn;
}
/// <summary>
/// 重写ToString方法,提供友好的字符串表示
/// </summary>
/// <returns>格式化的字符串</returns>
public override string ToString()
{
var imsiInfo = string.IsNullOrEmpty(Imsi) ? "" : $", IMSI: {Imsi}";
var rootInfo = RootUeId > 0 ? $", Root UE: {RootUeId}" : "";
var plmnInfo = string.IsNullOrEmpty(Plmn) ? "" : $", PLMN: {Plmn}";
return $"SR-TMSI: 0x{Tmsi:X8}, UE ID: {UeId}{imsiInfo}{rootInfo}{plmnInfo}";
}
}
}

6
CoreAgent.ProtocolClient/Models/ProtocolLogDetail.cs → CoreAgent.ProtocolClient/Models/TransferProtocolLog.cs

@ -5,11 +5,11 @@ using CoreAgent.ProtocolClient.Enums;
namespace CoreAgent.ProtocolClient.Models
{
/// <summary>
/// 协议日志详情实体
/// 用于存储解析后的协议日志详细信息到数据库
/// 传输协议日志模型
/// 用于传输给上层的协议日志数据
/// </summary>
[Table("ProtocolLogDetails")]
public class ProtocolLogDetail
public class TransferProtocolLog
{
/// <summary>
/// 主键ID

2
CoreAgent.ProtocolClient/Models/UeInfo.cs

@ -10,7 +10,7 @@ namespace CoreAgent.ProtocolClient.Models
/// 用户设备(UE)信息模型
/// 用于存储用户设备的基本信息,包括标识符、设备能力等
/// </summary>
public class UeInfo
public class UEInfo
{
/// <summary>
/// 用户设备唯一标识符

1127
CoreAgent.ProtocolClient/modify.md

File diff suppressed because it is too large
Loading…
Cancel
Save