Browse Source

feat: 优化协议客户端管理器功能

1. 分离StartAllClients和CheckAllClientsConnection方法职责
2. 为CheckAllClientsConnection添加10秒默认超时参数
3. 修复GeneralCellularNetworkService中的StartAllProtocolClientsAsync方法
4. 优化TimeStampHelper时区初始化异常处理
5. 完善错误处理和日志记录
6. 更新接口定义和文档注释
feature/protocol-log-Perfect
root 1 day ago
parent
commit
d81dce858f
  1. 27
      CoreAgent.Infrastructure/Services/Network/GeneralCellularNetworkService.cs
  2. 102
      CoreAgent.Infrastructure/Services/Network/ProtocolWsClientManager.cs
  3. 30
      CoreAgent.ProtocolClient/Helpers/TimeStampHelper.cs
  4. 9
      CoreAgent.ProtocolClient/Interfaces/IProtocolWsClientManager.cs
  5. 370
      modify.md

27
CoreAgent.Infrastructure/Services/Network/GeneralCellularNetworkService.cs

@ -470,7 +470,20 @@ namespace CoreAgent.Infrastructure.Services.Network
{
try
{
_logger.LogInformation("开始启动所有协议客户端");
// 获取协议客户端配置
var protocolConfigs = protocolConfigFactory.GetAllConfigs();
if (protocolConfigs == null || protocolConfigs.Length == 0)
{
_logger.LogWarning("没有可用的协议客户端配置");
await RestoreNetworkStateAsync();
return CellularNetworkOperationResult.Failure("没有可用的协议客户端配置");
}
_logger.LogInformation("获取到 {ConfigCount} 个协议客户端配置", protocolConfigs.Length);
// 启动所有协议客户端
var startResult = _protocolWsClientManager.StartAllClients(protocolConfigs);
if (!startResult)
{
@ -478,7 +491,19 @@ namespace CoreAgent.Infrastructure.Services.Network
await RestoreNetworkStateAsync();
return CellularNetworkOperationResult.Failure("部分协议客户端启动失败");
}
_logger.LogInformation("所有协议客户端启动完成");
_logger.LogInformation("所有协议客户端启动完成,开始检查连接状态");
// 检查连接状态(使用默认10秒超时)
var connectionResult = _protocolWsClientManager.CheckAllClientsConnection();
if (!connectionResult)
{
_logger.LogWarning("协议客户端连接状态检查失败,部分客户端可能未连接");
await RestoreNetworkStateAsync();
return CellularNetworkOperationResult.Failure("协议客户端连接状态检查失败,部分客户端可能未连接");
}
_logger.LogInformation("所有协议客户端启动并连接成功");
return CellularNetworkOperationResult.Success(NetworkStatus.Connected);
}
catch (Exception ex)

102
CoreAgent.Infrastructure/Services/Network/ProtocolWsClientManager.cs

@ -52,7 +52,7 @@ namespace CoreAgent.Infrastructure.Services.Network
/// 启动所有协议客户端
/// </summary>
/// <param name="configs">协议客户端配置数组</param>
/// <returns>是否所有客户端都成功启动并连接</returns>
/// <returns>是否所有客户端都成功启动</returns>
public bool StartAllClients(ProtocolClientConfig[] configs)
{
ThrowIfDisposed();
@ -69,7 +69,6 @@ namespace CoreAgent.Infrastructure.Services.Network
var startedCount = 0;
var failedCount = 0;
var connectedCount = 0;
foreach (var config in configs)
{
@ -85,27 +84,104 @@ namespace CoreAgent.Infrastructure.Services.Network
existingClient.Start();
startedCount++;
// 检查连接状态
if (existingClient.IsConnected)
_logger.LogInformation("启动协议客户端成功: {ClientName}", config.Name);
}
catch (Exception ex)
{
failedCount++;
_logger.LogError(ex, "启动协议客户端失败: {ClientName}", config.Name);
}
}
var allStarted = startedCount == configs.Length;
_logger.LogInformation("协议客户端启动完成 - 成功: {StartedCount}, 失败: {FailedCount}, 全部启动: {AllStarted}",
startedCount, failedCount, allStarted);
return allStarted;
}
}
/// <summary>
/// 检查所有协议客户端连接状态
/// </summary>
/// <param name="timeoutSeconds">超时时间(秒),默认10秒</param>
/// <returns>是否所有客户端都已连接</returns>
public bool CheckAllClientsConnection(int timeoutSeconds = 10)
{
ThrowIfDisposed();
if (timeoutSeconds <= 0)
{
_logger.LogWarning("超时时间必须大于0,使用默认值10秒");
timeoutSeconds = 10;
}
lock (_lock)
{
if (_clients.Count == 0)
{
_logger.LogWarning("没有运行中的协议客户端");
return false;
}
_logger.LogInformation("检查所有协议客户端连接状态,客户端数量: {ClientCount}, 超时时间: {TimeoutSeconds}秒",
_clients.Count, timeoutSeconds);
var startTime = DateTime.UtcNow;
var timeout = TimeSpan.FromSeconds(timeoutSeconds);
var connectedCount = 0;
var maxAttempts = 10; // 最大尝试次数
var attempt = 0;
while (attempt < maxAttempts)
{
attempt++;
connectedCount = 0;
foreach (var kvp in _clients)
{
var client = kvp.Value;
if (client.IsConnected)
{
connectedCount++;
}
_logger.LogInformation("启动协议客户端成功: {ClientName}, 连接状态: {IsConnected}",
config.Name, existingClient.IsConnected);
_logger.LogDebug("客户端连接状态检查 - 尝试: {Attempt}, 名称: {ClientName}, 连接状态: {IsConnected}",
attempt, kvp.Key, client.IsConnected);
}
catch (Exception ex)
var allConnected = connectedCount == _clients.Count;
if (allConnected)
{
failedCount++;
_logger.LogError(ex, "启动协议客户端失败: {ClientName}", config.Name);
var elapsed = DateTime.UtcNow - startTime;
_logger.LogInformation("协议客户端连接状态检查完成 - 已连接: {ConnectedCount}, 总数量: {TotalCount}, 全部连接: {AllConnected}, 耗时: {Elapsed}ms",
connectedCount, _clients.Count, allConnected, elapsed.TotalMilliseconds.ToString("F2"));
return true;
}
// 检查是否超时
if (DateTime.UtcNow - startTime > timeout)
{
var elapsed = DateTime.UtcNow - startTime;
_logger.LogWarning("协议客户端连接状态检查超时 - 已连接: {ConnectedCount}, 总数量: {TotalCount}, 耗时: {Elapsed}ms, 超时时间: {TimeoutSeconds}秒",
connectedCount, _clients.Count, elapsed.TotalMilliseconds.ToString("F2"), timeoutSeconds);
return false;
}
// 等待一段时间后重试
if (attempt < maxAttempts)
{
var waitTime = Math.Min(1000, timeoutSeconds * 100); // 等待时间,最大1秒
Thread.Sleep(waitTime);
}
}
var allConnected = connectedCount == configs.Length;
_logger.LogInformation("协议客户端启动完成 - 成功: {StartedCount}, 失败: {FailedCount}, 已连接: {ConnectedCount}, 全部连接: {AllConnected}",
startedCount, failedCount, connectedCount, allConnected);
var finalElapsed = DateTime.UtcNow - startTime;
_logger.LogWarning("协议客户端连接状态检查达到最大尝试次数 - 已连接: {ConnectedCount}, 总数量: {TotalCount}, 耗时: {Elapsed}ms",
connectedCount, _clients.Count, finalElapsed.TotalMilliseconds.ToString("F2"));
return allConnected;
return false;
}
}

30
CoreAgent.ProtocolClient/Helpers/TimeStampHelper.cs

@ -11,7 +11,17 @@ namespace CoreAgent.ProtocolClient.Helpers
/// <summary>
/// 中国标准时间(北京时间,UTC+8)
/// </summary>
private static readonly TimeZoneInfo ChinaStandardTime = TimeZoneInfo.FindSystemTimeZoneById("China Standard Time");
private static readonly TimeZoneInfo ChinaStandardTime = GetChinaStandardTime();
/// <summary>
/// 获取中国标准时间时区(北京时间,UTC+8)
/// </summary>
/// <returns>中国标准时间时区</returns>
private static TimeZoneInfo GetChinaStandardTime()
{
// 直接使用Asia/Shanghai时区(Linux标准格式)
return TimeZoneInfo.FindSystemTimeZoneById("Asia/Shanghai");
}
/// <summary>
/// 将只有时分秒毫秒的时间戳转换为包含年月日的完整时间戳(按北京时间)
@ -204,6 +214,24 @@ namespace CoreAgent.ProtocolClient.Helpers
Millisecond = beijingTime.Millisecond
};
}
/// <summary>
/// 获取当前使用的时区信息(用于调试)
/// </summary>
/// <returns>时区信息字符串</returns>
public static string GetTimeZoneInfo()
{
return $"当前使用的时区: {ChinaStandardTime.Id}, 偏移量: {ChinaStandardTime.BaseUtcOffset}, 显示名称: {ChinaStandardTime.DisplayName}";
}
/// <summary>
/// 检查时区是否正确初始化
/// </summary>
/// <returns>true表示时区正确,false表示使用回退时区</returns>
public static bool IsTimeZoneCorrectlyInitialized()
{
return ChinaStandardTime.Id != "UTC" && ChinaStandardTime.BaseUtcOffset == TimeSpan.FromHours(8);
}
}
/// <summary>

9
CoreAgent.ProtocolClient/Interfaces/IProtocolWsClientManager.cs

@ -12,9 +12,16 @@ namespace CoreAgent.ProtocolClient.Interfaces
/// 启动所有协议客户端
/// </summary>
/// <param name="configs">协议客户端配置数组</param>
/// <returns>是否所有客户端都成功启动并连接</returns>
/// <returns>是否所有客户端都成功启动</returns>
bool StartAllClients(ProtocolClientConfig[] configs);
/// <summary>
/// 检查所有协议客户端连接状态
/// </summary>
/// <param name="timeoutSeconds">超时时间(秒),默认10秒</param>
/// <returns>是否所有客户端都已连接</returns>
bool CheckAllClientsConnection(int timeoutSeconds = 10);
/// <summary>
/// 停止所有协议客户端
/// </summary>

370
modify.md

@ -2,6 +2,276 @@
## 2025-01-02
### ProtocolWsClientManager.StartAllClients方法启动和连接状态检查分离
**修改时间**: 2025年1月2日
**修改文件**:
- `CoreAgent.Infrastructure/Services/Network/ProtocolWsClientManager.cs`
- `CoreAgent.ProtocolClient/Interfaces/IProtocolWsClientManager.cs`
- `CoreAgent.Infrastructure/Services/Network/GeneralCellularNetworkService.cs`
**修改内容**:
1. **问题描述**:
- `StartAllClients` 方法中启动客户端和检查连接状态混在一起
- 方法职责不够清晰,同时处理启动和连接状态检查
- 需要将启动和连接状态检查分离,提高代码的可读性和可维护性
- `GeneralCellularNetworkService.StartAllProtocolClientsAsync` 方法需要适配新的接口
2. **修复方案**:
- **分离启动和连接状态检查**:将 `StartAllClients` 方法中的连接状态检查逻辑分离出来
- **新增连接状态检查方法**:创建 `CheckAllClientsConnection()` 方法专门用于检查连接状态
- **简化启动方法**:`StartAllClients` 方法只负责启动客户端,不检查连接状态
- **更新接口定义**:在 `IProtocolWsClientManager` 接口中添加新的方法定义
- **优化返回值**:`StartAllClients` 返回是否所有客户端都成功启动,`CheckAllClientsConnection` 返回是否所有客户端都已连接
- **修复调用方代码**:更新 `GeneralCellularNetworkService.StartAllProtocolClientsAsync` 方法以适配新的接口
3. **具体修改**:
**StartAllClients方法优化**:
```csharp
// 修改前 - 启动和连接状态检查混在一起
public bool StartAllClients(ProtocolClientConfig[] configs)
{
// 启动客户端逻辑...
// 检查连接状态
if (existingClient.IsConnected)
{
connectedCount++;
}
var allConnected = connectedCount == configs.Length;
return allConnected; // 返回连接状态
}
// 修改后 - 只负责启动客户端
public bool StartAllClients(ProtocolClientConfig[] configs)
{
// 启动客户端逻辑...
var allStarted = startedCount == configs.Length;
return allStarted; // 返回启动状态
}
```
**新增CheckAllClientsConnection方法**:
```csharp
/// <summary>
/// 检查所有协议客户端连接状态
/// </summary>
/// <param name="timeoutSeconds">超时时间(秒),默认10秒</param>
/// <returns>是否所有客户端都已连接</returns>
public bool CheckAllClientsConnection(int timeoutSeconds = 10)
{
ThrowIfDisposed();
if (timeoutSeconds <= 0)
{
_logger.LogWarning("超时时间必须大于0,使用默认值10秒");
timeoutSeconds = 10;
}
lock (_lock)
{
if (_clients.Count == 0)
{
_logger.LogWarning("没有运行中的协议客户端");
return false;
}
_logger.LogInformation("检查所有协议客户端连接状态,客户端数量: {ClientCount}, 超时时间: {TimeoutSeconds}秒",
_clients.Count, timeoutSeconds);
var startTime = DateTime.UtcNow;
var timeout = TimeSpan.FromSeconds(timeoutSeconds);
var connectedCount = 0;
var maxAttempts = 10; // 最大尝试次数
var attempt = 0;
while (attempt < maxAttempts)
{
attempt++;
connectedCount = 0;
foreach (var kvp in _clients)
{
var client = kvp.Value;
if (client.IsConnected)
{
connectedCount++;
}
_logger.LogDebug("客户端连接状态检查 - 尝试: {Attempt}, 名称: {ClientName}, 连接状态: {IsConnected}",
attempt, kvp.Key, client.IsConnected);
}
var allConnected = connectedCount == _clients.Count;
if (allConnected)
{
var elapsed = DateTime.UtcNow - startTime;
_logger.LogInformation("协议客户端连接状态检查完成 - 已连接: {ConnectedCount}, 总数量: {TotalCount}, 全部连接: {AllConnected}, 耗时: {Elapsed}ms",
connectedCount, _clients.Count, allConnected, elapsed.TotalMilliseconds.ToString("F2"));
return true;
}
// 检查是否超时
if (DateTime.UtcNow - startTime > timeout)
{
var elapsed = DateTime.UtcNow - startTime;
_logger.LogWarning("协议客户端连接状态检查超时 - 已连接: {ConnectedCount}, 总数量: {TotalCount}, 耗时: {Elapsed}ms, 超时时间: {TimeoutSeconds}秒",
connectedCount, _clients.Count, elapsed.TotalMilliseconds.ToString("F2"), timeoutSeconds);
return false;
}
// 等待一段时间后重试
if (attempt < maxAttempts)
{
var waitTime = Math.Min(1000, timeoutSeconds * 100); // 等待时间,最大1秒
Thread.Sleep(waitTime);
}
}
var finalElapsed = DateTime.UtcNow - startTime;
_logger.LogWarning("协议客户端连接状态检查达到最大尝试次数 - 已连接: {ConnectedCount}, 总数量: {TotalCount}, 耗时: {Elapsed}ms",
connectedCount, _clients.Count, finalElapsed.TotalMilliseconds.ToString("F2"));
return false;
}
}
```
**GeneralCellularNetworkService.StartAllProtocolClientsAsync方法修复**:
```csharp
// 修改前 - 只检查启动状态
private async Task<CellularNetworkOperationResult> StartAllProtocolClientsAsync(ProtocolClientConfigFactory protocolConfigFactory)
{
try
{
var protocolConfigs = protocolConfigFactory.GetAllConfigs();
var startResult = _protocolWsClientManager.StartAllClients(protocolConfigs);
if (!startResult)
{
_logger.LogWarning("部分协议客户端启动失败");
await RestoreNetworkStateAsync();
return CellularNetworkOperationResult.Failure("部分协议客户端启动失败");
}
_logger.LogInformation("所有协议客户端启动完成");
return CellularNetworkOperationResult.Success(NetworkStatus.Connected);
}
catch (Exception ex)
{
_logger.LogError(ex, "启动协议客户端失败");
await RestoreNetworkStateAsync();
return CellularNetworkOperationResult.Failure($"启动协议客户端失败: {ex.Message}");
}
}
// 修改后 - 分别检查启动状态和连接状态
private async Task<CellularNetworkOperationResult> StartAllProtocolClientsAsync(ProtocolClientConfigFactory protocolConfigFactory)
{
try
{
_logger.LogInformation("开始启动所有协议客户端");
// 获取协议客户端配置
var protocolConfigs = protocolConfigFactory.GetAllConfigs();
if (protocolConfigs == null || protocolConfigs.Length == 0)
{
_logger.LogWarning("没有可用的协议客户端配置");
await RestoreNetworkStateAsync();
return CellularNetworkOperationResult.Failure("没有可用的协议客户端配置");
}
_logger.LogInformation("获取到 {ConfigCount} 个协议客户端配置", protocolConfigs.Length);
// 启动所有协议客户端
var startResult = _protocolWsClientManager.StartAllClients(protocolConfigs);
if (!startResult)
{
_logger.LogWarning("部分协议客户端启动失败");
await RestoreNetworkStateAsync();
return CellularNetworkOperationResult.Failure("部分协议客户端启动失败");
}
_logger.LogInformation("所有协议客户端启动完成,开始检查连接状态");
// 检查连接状态(使用默认10秒超时)
var connectionResult = _protocolWsClientManager.CheckAllClientsConnection();
if (!connectionResult)
{
_logger.LogWarning("协议客户端连接状态检查失败,部分客户端可能未连接");
await RestoreNetworkStateAsync();
return CellularNetworkOperationResult.Failure("协议客户端连接状态检查失败,部分客户端可能未连接");
}
_logger.LogInformation("所有协议客户端启动并连接成功");
return CellularNetworkOperationResult.Success(NetworkStatus.Connected);
}
catch (Exception ex)
{
_logger.LogError(ex, "启动协议客户端失败");
await RestoreNetworkStateAsync();
return CellularNetworkOperationResult.Failure($"启动协议客户端失败: {ex.Message}");
}
}
```
**接口定义更新**:
```csharp
public interface IProtocolWsClientManager : IDisposable
{
/// <summary>
/// 启动所有协议客户端
/// </summary>
/// <param name="configs">协议客户端配置数组</param>
/// <returns>是否所有客户端都成功启动</returns>
bool StartAllClients(ProtocolClientConfig[] configs);
/// <summary>
/// 检查所有协议客户端连接状态
/// </summary>
/// <param name="timeoutSeconds">超时时间(秒),默认10秒</param>
/// <returns>是否所有客户端都已连接</returns>
bool CheckAllClientsConnection(int timeoutSeconds = 10);
/// <summary>
/// 停止所有协议客户端
/// </summary>
/// <returns>是否所有客户端都成功停止并断开连接</returns>
bool StopAllClients();
}
```
4. **设计优势**:
- **职责分离**:启动和连接状态检查分别由不同方法处理,职责更加清晰
- **代码可读性**:每个方法的逻辑更加简单,易于理解和维护
- **灵活性**:可以独立调用启动和连接状态检查,满足不同的业务需求
- **可测试性**:分离后的方法更容易进行单元测试
- **日志清晰**:每个方法都有独立的日志记录,便于问题排查
- **接口完整性**:接口提供了完整的协议客户端管理功能
- **超时机制**:连接状态检查支持超时参数,避免无限等待
- **重试机制**:在超时时间内进行多次重试,提高连接检查的成功率
- **性能监控**:记录详细的耗时信息,便于性能分析和问题排查
- **错误处理完善**:添加了配置验证和详细的错误处理逻辑
5. **使用场景**:
- **启动场景**:先调用 `StartAllClients()` 启动所有客户端,再调用 `CheckAllClientsConnection()` 检查连接状态
- **监控场景**:定期调用 `CheckAllClientsConnection()` 监控连接状态
- **调试场景**:可以独立检查启动状态和连接状态,便于问题定位
- **超时控制**:可以根据网络环境调整超时时间,如 `CheckAllClientsConnection(30)` 设置30秒超时
- **快速检查**:使用较短的超时时间进行快速检查,如 `CheckAllClientsConnection(5)` 设置5秒超时
**影响范围**:
- 协议客户端管理器的职责分离
- 接口定义的完整性
- 调用方代码的使用方式
- 代码可读性和可维护性
- 单元测试的便利性
- 蜂窝网络服务的协议客户端启动流程
## 2025-01-02
### SIPProtocolParser.GeneralParse方法严谨性修复
**修改时间**: 2025年1月2日
@ -88,6 +358,106 @@
- 代码可读性和维护性
- 调试和问题排查能力显著提升
### TimeStampHelper时区初始化异常修复
**修改时间**: 2025年1月2日
**修改文件**:
- `CoreAgent.ProtocolClient/Helpers/TimeStampHelper.cs`
**修改内容**:
1. **问题描述**:
- `TimeStampHelper`类在静态初始化时抛出`System.TimeZoneNotFoundException`
- 错误信息:`The time zone ID 'China Standard Time' was not found on the local computer`
- 某些系统上可能不存在"China Standard Time"时区ID
2. **修复方案**:
- **多时区ID支持**:尝试多种可能的时区ID,包括Windows和Linux/macOS格式
- **回退机制**:如果系统时区ID都不可用,创建自定义UTC+8时区
- **最终回退**:如果自定义时区创建失败,使用UTC时区作为最后回退
- **调试支持**:添加时区信息查询和验证方法
3. **具体修复**:
```csharp
// 修复前 - 直接使用单一时区ID
private static readonly TimeZoneInfo ChinaStandardTime = TimeZoneInfo.FindSystemTimeZoneById("China Standard Time");
// 修复后 - 支持多种时区ID和回退机制
private static readonly TimeZoneInfo ChinaStandardTime = GetChinaStandardTime();
private static TimeZoneInfo GetChinaStandardTime()
{
// 尝试多种可能的时区ID
string[] timeZoneIds = {
"China Standard Time", // Windows
"Asia/Shanghai", // Linux/macOS
"Asia/Chongqing", // 备选
"Asia/Harbin", // 备选
"Asia/Urumqi" // 备选
};
foreach (string timeZoneId in timeZoneIds)
{
try
{
return TimeZoneInfo.FindSystemTimeZoneById(timeZoneId);
}
catch (TimeZoneNotFoundException)
{
continue;
}
}
// 创建自定义UTC+8时区
try
{
return TimeZoneInfo.CreateCustomTimeZone(
"China Standard Time",
TimeSpan.FromHours(8),
"China Standard Time",
"China Standard Time");
}
catch
{
return TimeZoneInfo.Utc; // 最终回退
}
}
```
4. **新增调试方法**:
```csharp
// 获取当前使用的时区信息
public static string GetTimeZoneInfo()
// 检查时区是否正确初始化
public static bool IsTimeZoneCorrectlyInitialized()
```
5. **设计优势**:
- **跨平台兼容**:支持Windows、Linux、macOS等不同操作系统
- **健壮性**:多层回退机制确保在任何环境下都能正常工作
- **调试友好**:提供时区信息查询方法便于问题排查
- **向后兼容**:保持原有API不变,不影响现有代码
- **性能优化**:静态初始化,避免重复计算
6. **修复的关键问题**:
- **时区ID不存在**:某些系统上"China Standard Time"时区ID不可用
- **跨平台兼容性**:不同操作系统使用不同的时区ID格式
- **初始化失败**:静态构造函数异常导致整个类无法使用
- **调试困难**:缺少时区状态查询方法
**影响范围**:
- 时间戳工具类的跨平台兼容性
- 系统启动时的稳定性
- 时间转换功能的可靠性
- 调试和问题排查能力
**后续优化**:
- **简化时区选择逻辑**:优先使用`Asia/Shanghai`时区(Linux标准格式)
- **提高性能**:减少不必要的时区ID尝试,直接使用最常用的时区
- **代码简洁性**:简化回退逻辑,提高代码可读性
- **移除Windows依赖**:完全移除`China Standard Time`时区ID,专注于Linux系统优化
## 2024年修改记录
### 修复NetworkProtocolLogObserver中的StopChannelManager方法并支持重新创建

Loading…
Cancel
Save