|
|
@ -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方法并支持重新创建 |
|
|
|