From f4aebca633248a5eb0089ab42544b5a37c5b615a Mon Sep 17 00:00:00 2001 From: zqm Date: Mon, 27 Oct 2025 15:36:18 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=9E=E7=8E=B0=E5=BF=83=E8=B7=B3=E6=A3=80?= =?UTF-8?q?=E6=B5=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Toprie/Toprie/DeviceManager.cs | 676 ++++++++++++++---- Windows/CS/Framework4.0/Toprie/Toprie/V8.cs | 70 +- 2 files changed, 584 insertions(+), 162 deletions(-) diff --git a/Windows/CS/Framework4.0/Toprie/Toprie/DeviceManager.cs b/Windows/CS/Framework4.0/Toprie/Toprie/DeviceManager.cs index f755a84..fce6266 100644 --- a/Windows/CS/Framework4.0/Toprie/Toprie/DeviceManager.cs +++ b/Windows/CS/Framework4.0/Toprie/Toprie/DeviceManager.cs @@ -148,6 +148,7 @@ namespace JoyD.Windows.CS.Toprie private System.Threading.Timer _reconnectTimer; // 重连间隔(毫秒) private int _reconnectInterval = 2000; + private int _connectionCheckInterval = 5000; // 重连尝试次数 private int _reconnectAttempts = 0; // 最大重连尝试次数 @@ -198,6 +199,25 @@ namespace JoyD.Windows.CS.Toprie private Thread _imageReceiveThread; private Thread _imageReconnectThread; private Stream _imageStream; + + /// + /// 检查网络是否可用 + /// + /// 网络是否可用 + private bool IsNetworkAvailable() + { + try + { + bool isAvailable = System.Net.NetworkInformation.NetworkInterface.GetIsNetworkAvailable(); + Console.WriteLine($"网络可用性检查: {(isAvailable ? "可用" : "不可用")}"); + return isAvailable; + } + catch (Exception ex) + { + Console.WriteLine($"网络检查异常: {ex.Message}"); + return false; + } + } private TcpClient _imageTcpClient; private bool _isInfraredMode = true; @@ -295,38 +315,129 @@ namespace JoyD.Windows.CS.Toprie /// 是否初始化成功 private bool Initialize() { + // 双重检查锁定模式,确保线程安全的初始化 if (_isInitialized) return true; try { - // 初始化设备ID列表 - _deviceIds = new List(); // 保持与类中定义一致 - _reconnectTimer = null; - _heartbeatTimer = null; - _connectionCheckTimer = null; + // 清理旧资源(如果有) + CleanupResources(); + + // 初始化设备ID列表和相关变量 + _deviceIds = new List(); + _currentDeviceId = -1; + // 保留默认设备IP,不要重置为空字符串 + // _deviceIp = string.Empty; // 注释掉这行,避免IP地址被清空 + _connectionStatus = ConnectionStatus.Disconnected; + _currentReconnectAttempt = 0; + + Console.WriteLine("开始SDK初始化..."); + + // 首先检查网络可用性 + if (!IsNetworkAvailable()) + { + Console.WriteLine("网络不可用,初始化暂缓"); + return false; + } // 首先调用SDK静态初始化方法 - 这是连接成功的关键步骤! - int initResult = A8SDK.SDK_initialize(); + // 添加重试机制,增强初始化可靠性 + int initResult = -1; + int maxInitRetries = 2; + + for (int retry = 0; retry <= maxInitRetries; retry++) + { + try + { + if (retry > 0) + { + Console.WriteLine($"SDK初始化重试 {retry}/{maxInitRetries}"); + Thread.Sleep(300); // 重试前等待一小段时间 + } + + initResult = A8SDK.SDK_initialize(); + if (initResult == 0) + { + break; // 成功,跳出循环 + } + + Console.WriteLine($"SDK静态初始化失败,返回值: {initResult}"); + } + catch (Exception initEx) + { + Console.WriteLine($"SDK初始化异常: {initEx.Message},堆栈: {initEx.StackTrace}"); + } + } + if (initResult != 0) { - Console.WriteLine($"SDK静态初始化失败,返回值: {initResult}"); + Console.WriteLine($"SDK静态初始化失败,所有重试均未成功,返回值: {initResult}"); _isInitialized = false; return false; } Console.WriteLine("SDK静态初始化成功"); + + // 设置默认配置参数 + _maxReconnectAttempts = 15; // 增加最大重连次数 + _isAutoReconnectEnabled = true; + _isInitialized = true; return true; } catch (Exception ex) { - Console.WriteLine($"SDK初始化失败: {ex.Message}"); + Console.WriteLine($"SDK初始化失败: {ex.Message},堆栈: {ex.StackTrace}"); _isInitialized = false; OnConnectionException(new ConnectionExceptionEventArgs(ex, "初始化设备管理器失败")); return false; } } + + /// + /// 清理所有资源,确保安全释放 + /// + private void CleanupResources() + { + try + { + // 停止所有定时器 + StopAutoReconnect(); + StopHeartbeat(); + StopConnectionCheck(); + + // 安全释放SDK实例 + A8SDK oldSdk = Interlocked.Exchange(ref _a8Sdk, null); + if (oldSdk != null) + { + try + { + Console.WriteLine("释放旧SDK实例..."); + // 注意:如果SDK提供了destroy方法,应该在这里调用 + // A8SDK.SDK_destroy(); + } + catch (Exception ex) + { + Console.WriteLine($"释放SDK实例异常: {ex.Message}"); + } + } + + // 重置状态变量 + _deviceIds = null; + _connectionStatus = ConnectionStatus.Disconnected; + _currentDeviceId = -1; + // _deviceIp = string.Empty; // 注释掉这行,避免IP地址在资源清理时被清空 + _currentReconnectAttempt = 0; + _isInitialized = false; + + Console.WriteLine("资源清理完成"); + } + catch (Exception ex) + { + Console.WriteLine($"清理资源异常: {ex.Message}"); + } + } /// /// 尝试ping设备IP地址 @@ -337,18 +448,37 @@ namespace JoyD.Windows.CS.Toprie { try { - // 检查系统网络连接状态 + // 尝试真正的ICMP ping,提供更准确的设备可达性检查 + using (var ping = new System.Net.NetworkInformation.Ping()) + { + try + { + Console.WriteLine($"正在ping设备IP: {ipAddress},检查网络可达性..."); + var reply = ping.Send(ipAddress, 2000); // 增加超时时间到2秒 + bool isReachable = reply != null && reply.Status == System.Net.NetworkInformation.IPStatus.Success; + Console.WriteLine($"Ping结果: {(isReachable ? "成功" : "失败")}"); + return isReachable; + } + catch (Exception pingEx) + { + Console.WriteLine($"Ping异常: {pingEx.Message}"); + // 如果ICMP ping失败,退回到原有的网络接口检查逻辑 + Console.WriteLine("尝试网络接口检查作为备选方案..."); + } + } + + // 改进的网络接口检查逻辑 System.Net.NetworkInformation.NetworkInterface[] interfaces = System.Net.NetworkInformation.NetworkInterface.GetAllNetworkInterfaces(); // 检查是否有活动的以太网或WiFi连接 foreach (var ni in interfaces) { - // 仅检查物理网络接口(以太网、WiFi等) + // 仅检查活动的网络接口,移除过于严格的过滤条件 if (ni.OperationalStatus == System.Net.NetworkInformation.OperationalStatus.Up && (ni.NetworkInterfaceType == System.Net.NetworkInformation.NetworkInterfaceType.Ethernet || - ni.NetworkInterfaceType == System.Net.NetworkInformation.NetworkInterfaceType.Wireless80211) && - ni.Name != "本地连接*" && !ni.Description.Contains("虚拟") && !ni.Description.Contains("Virtual")) + ni.NetworkInterfaceType == System.Net.NetworkInformation.NetworkInterfaceType.Wireless80211 || + ni.NetworkInterfaceType == System.Net.NetworkInformation.NetworkInterfaceType.GigabitEthernet)) { // 获取该网络接口的IP信息,检查是否有有效的IP地址 var properties = ni.GetIPProperties(); @@ -362,20 +492,44 @@ namespace JoyD.Windows.CS.Toprie { try { - // 检查设备IP是否与当前网络接口在同一子网(粗略判断) + // 使用更准确的子网判断方法 var deviceAddress = System.Net.IPAddress.Parse(_deviceIp); - if (deviceAddress.GetAddressBytes()[0] == addr.Address.GetAddressBytes()[0]) + var localAddress = addr.Address; + + // 获取子网掩码 + byte[] subnetMaskBytes = addr.IPv4Mask.GetAddressBytes(); + byte[] deviceAddressBytes = deviceAddress.GetAddressBytes(); + byte[] localAddressBytes = localAddress.GetAddressBytes(); + + // 检查是否在同一子网(使用子网掩码进行计算) + bool isInSameSubnet = true; + for (int i = 0; i < subnetMaskBytes.Length; i++) { + if ((deviceAddressBytes[i] & subnetMaskBytes[i]) != + (localAddressBytes[i] & subnetMaskBytes[i])) + { + isInSameSubnet = false; + break; + } + } + + if (isInSameSubnet) + { + Console.WriteLine($"设备IP与本地网络接口在同一网段: {localAddress}"); return true; } } - catch { } + catch (Exception ex) + { + Console.WriteLine($"子网判断异常: {ex.Message}"); + } } return true; } } } } + Console.WriteLine("未找到有效的网络连接或设备IP不在同一网段"); return false; } catch (Exception ex) @@ -595,18 +749,68 @@ namespace JoyD.Windows.CS.Toprie // 优化3: 加强SDK连接状态验证,参考热像仪示例的实现方式 try { - // 确保SDK实例存在 + // 使用线程安全的SDK实例创建 if (_a8Sdk == null) { Console.WriteLine("SDK实例不存在,重新初始化..."); _a8Sdk = new A8SDK(_deviceIp); } - // 简化心跳检测逻辑,避免在心跳失败时过度访问可能无效的资源 - int heartbeatResult = _a8Sdk.Heartbeat(); - if (heartbeatResult <= 0) + // 增强心跳检测重试机制,提高连接稳定性 + int maxHeartbeatRetries = 3; // 增加到3次重试 + int heartbeatResult = -1; + + for (int retry = 0; retry <= maxHeartbeatRetries; retry++) + { + try + { + Console.WriteLine($"SDK心跳检测...(尝试 {retry + 1}/{maxHeartbeatRetries + 1})"); + heartbeatResult = _a8Sdk.Heartbeat(); + + // 严格检查返回值,确保连接有效 + if (heartbeatResult == 0) // 参考Toprie项目,假设0表示成功 + { + Console.WriteLine("SDK心跳检测成功"); + break; + } + else if (retry < maxHeartbeatRetries) + { + Console.WriteLine($"SDK心跳检测失败,返回代码: {heartbeatResult},等待500ms后重试..."); + Thread.Sleep(500); // 增加重试间隔到500ms + } + else + { + Console.WriteLine("SDK心跳检测多次失败,尝试重建SDK连接..."); + // 安全释放旧的SDK实例 + A8SDK oldSdk = Interlocked.Exchange(ref _a8Sdk, null); + // 尝试重建SDK连接 + Thread.Sleep(500); + _a8Sdk = new A8SDK(_deviceIp); + Thread.Sleep(500); + heartbeatResult = _a8Sdk.Heartbeat(); + + if (heartbeatResult == 0) + { + Console.WriteLine("SDK重新连接成功"); + } + else + { + Console.WriteLine($"SDK重新连接失败,返回代码: {heartbeatResult}"); + } + } + } + catch (Exception ex) + { + Console.WriteLine($"心跳检测异常: {ex.Message}"); + if (retry < maxHeartbeatRetries) + { + Thread.Sleep(500); // 增加重试间隔到500ms + } + } + } + + if (heartbeatResult != 0) { - Console.WriteLine("SDK心跳检测失败"); // 心跳失败时,安全释放SDK实例 if (_a8Sdk != null) { @@ -614,7 +818,26 @@ namespace JoyD.Windows.CS.Toprie } return false; } - Console.WriteLine("SDK心跳检测成功"); + + // 添加额外的连接验证步骤 + try + { + Console.WriteLine("进行额外连接验证..."); + // 再次发送心跳包确保连接稳定 + int finalResult = _a8Sdk.Heartbeat(); + if (finalResult != 0) + { + Console.WriteLine($"最终验证失败,返回代码: {finalResult}"); + _a8Sdk = null; + return false; + } + } + catch (Exception ex) + { + Console.WriteLine($"连接验证异常: {ex.Message}"); + _a8Sdk = null; + return false; + } // 设备名称获取使用try-catch,避免在心跳成功但设备响应异常时崩溃 try @@ -635,6 +858,8 @@ namespace JoyD.Windows.CS.Toprie catch (Exception ex) { Console.WriteLine($"SDK心跳检测失败: {ex.Message}"); + // 确保异常时SDK实例被释放 + _a8Sdk = null; return false; } @@ -644,6 +869,8 @@ namespace JoyD.Windows.CS.Toprie catch (Exception ex) { Console.WriteLine($"连接有效性检查异常: {ex.Message}"); + // 确保异常时SDK实例被释放 + _a8Sdk = null; return false; } } @@ -694,6 +921,22 @@ namespace JoyD.Windows.CS.Toprie #region 公共属性 + /// + /// 获取或设置设备IP地址 + /// + public string IPAddress + { + get { return _deviceIp; } + set + { + if (!string.IsNullOrEmpty(value)) + { + _deviceIp = value; + Console.WriteLine($"设备IP地址已设置为: {_deviceIp}"); + } + } + } + /// /// 当前图像模式 /// @@ -1749,6 +1992,17 @@ namespace JoyD.Windows.CS.Toprie return; } + // 检查是否正在重连中,如果是则不执行连接 + if (_isReconnecting != 0) + { + Console.WriteLine("检测到正在重连过程中,暂停设备连接"); + UpdateConnectionStatus(ConnectionStatus.Disconnected, "正在进行自动重连,请稍后再试"); + return; + } + + // 设置连接中标志 + _isConnecting = true; + // 停止任何现有的重连定时器 StopAutoReconnect(); @@ -1774,35 +2028,56 @@ namespace JoyD.Windows.CS.Toprie } // 使用真实SDK连接设备 - Console.WriteLine($"正在使用SDK连接设备 {deviceId},IP地址: {_deviceIp}"); - - // 先检测IP可达性,避免不必要的SDK初始化 - if (!PingDevice(_deviceIp)) - { - Console.WriteLine($"设备IP {_deviceIp} 不可达,连接失败"); - throw new Exception($"设备IP {_deviceIp} 不可达"); - } - - // 创建SDK实例 - _a8Sdk = new A8SDK(_deviceIp); - Console.WriteLine("SDK实例创建完成"); - - // 验证连接是否成功(通过心跳检测) - if (_a8Sdk.Heartbeat() <= 0) - { - Console.WriteLine("心跳检测失败"); - // 安全释放SDK实例 - if (_a8Sdk != null) - { - _a8Sdk = null; - } - throw new Exception("心跳检测失败"); - } - Console.WriteLine("心跳检测成功,设备连接有效"); + Console.WriteLine($"正在使用SDK连接设备 {deviceId},IP地址: {_deviceIp},端口: {_devicePort}"); + + // 先检测IP可达性,避免不必要的SDK初始化 + if (string.IsNullOrEmpty(_deviceIp)) + { + Console.WriteLine("设备IP地址为空,请先设置有效的IP地址"); + throw new Exception("设备IP地址为空,请先设置有效的IP地址"); + } + else if (!PingDevice(_deviceIp)) + { + Console.WriteLine($"设备IP {_deviceIp} 不可达,连接失败"); + throw new Exception($"设备IP {_deviceIp} 不可达,请检查网络连接"); + } + + // 创建SDK实例 + _a8Sdk = new A8SDK(_deviceIp); + Console.WriteLine("SDK实例创建完成"); + + // 验证连接是否成功(通过心跳检测) + Console.WriteLine("尝试发送心跳包进行连接验证...(尝试 1/3)"); + int heartbeatResult = _a8Sdk.Heartbeat(); + + // 尝试多次心跳检测,增加连接成功率 + int retryCount = 1; + while (heartbeatResult <= 0 && retryCount < 3) + { + retryCount++; + Console.WriteLine($"心跳检测失败,等待500ms后重试...(尝试 {retryCount}/3)"); + Thread.Sleep(500); + heartbeatResult = _a8Sdk.Heartbeat(); + } + + if (heartbeatResult <= 0) + { + Console.WriteLine("多次心跳检测均失败"); + // 安全释放SDK实例 + if (_a8Sdk != null) + { + _a8Sdk = null; + } + throw new Exception("心跳检测失败,设备可能未响应或端口配置错误"); + } + Console.WriteLine("心跳检测成功,设备连接有效"); // 连接成功 result = true; + // 重置连接中标志 + _isConnecting = false; + // 安全地设置设备ID try { @@ -1824,6 +2099,8 @@ namespace JoyD.Windows.CS.Toprie { _a8Sdk = null; } + // 重置连接中标志 + _isConnecting = false; result = false; } finally @@ -2022,13 +2299,18 @@ namespace JoyD.Windows.CS.Toprie /// /// 开始自动重连 /// + // 重入保护标志 + private volatile int _isReconnecting = 0; + // 连接进行中标志(用于防止重连期间再次触发连接) + private volatile bool _isConnecting = false; + private void StartAutoReconnect() { // 停止现有的重连定时器 StopAutoReconnect(); - // 创建新的重连定时器 - _reconnectTimer = new System.Threading.Timer(ReconnectCallback, null, 0, _reconnectInterval); + // 创建新的重连定时器 - 使用一次性定时器而非重复定时器 + _reconnectTimer = new System.Threading.Timer(ReconnectCallback, null, 0, Timeout.Infinite); } /// @@ -2104,10 +2386,44 @@ namespace JoyD.Windows.CS.Toprie /// 定时器状态 private void ReconnectCallback(object state) { + // 使用Interlocked.Exchange实现原子操作检查,防止重入 + if (Interlocked.Exchange(ref _isReconnecting, 1) != 0) + { + Console.WriteLine("检测到重连回调正在执行,避免重入"); + return; + } + try { + // 检查是否已达到最大重连次数 + if (_currentReconnectAttempt >= _maxReconnectAttempts) + { + Console.WriteLine($"已达到最大重连次数 ({_maxReconnectAttempts}),停止自动重连"); + StopAutoReconnect(); + UpdateConnectionStatus(ConnectionStatus.Disconnected, "已达到最大重连次数,请手动检查设备状态"); + return; + } + + // 改进的重连间隔递增策略,避免指数增长过快 + int currentInterval = Math.Min(_reconnectInterval * (int)(1 + _currentReconnectAttempt * 0.5), 30000); // 最多30秒 + _currentReconnectAttempt++; - Console.WriteLine($"自动重连尝试 {_currentReconnectAttempt}"); + Console.WriteLine($"自动重连尝试 {_currentReconnectAttempt}/{_maxReconnectAttempts},当前间隔: {currentInterval}ms"); + + // 在重试前先检查网络状态 + if (!IsNetworkAvailable()) + { + Console.WriteLine("网络不可用,推迟重连尝试"); + UpdateConnectionStatus(ConnectionStatus.Disconnected, "网络连接不可用,请检查网络设置"); + + // 网络不可用时,调整为重试间隔较长 + int networkDownInterval = Math.Min(_reconnectInterval * 3, 15000); + if (_reconnectTimer != null) + { + _reconnectTimer.Change(networkDownInterval, Timeout.Infinite); + } + return; + } UpdateConnectionStatus(ConnectionStatus.Reconnecting, $"尝试自动重连..."); @@ -2120,33 +2436,29 @@ namespace JoyD.Windows.CS.Toprie Console.WriteLine($"用IP地址 {_deviceIp} 重连设备"); try { - // 检查IP是否可达 + // 检查IP是否可达,使用改进的PingDevice方法 if (PingDevice(_deviceIp)) { - // 注意:这里需要先释放旧实例,避免资源泄漏 - if (_a8Sdk != null) + // 使用Interlocked.Exchange确保线程安全地释放旧实例 + A8SDK oldSdk = Interlocked.Exchange(ref _a8Sdk, null); + if (oldSdk != null) { try { - // 调用静态SDK_destroy方法释放全局资源 - // A8SDK.SDK_destroy(); - Console.WriteLine("重连前SDK全局资源已释放"); + Console.WriteLine("安全释放旧SDK实例资源"); + // 注意:根据SDK文档,如果有destroy方法应调用 } catch (Exception ex) { Console.WriteLine($"释放旧SDK实例资源时发生异常: {ex.Message}"); } - finally - { - _a8Sdk = null; // 确保引用被清空 - } } // 创建新的SDK实例 Console.WriteLine($"创建新的SDK实例,目标IP: {_deviceIp}"); - _a8Sdk = new A8SDK(_deviceIp); + A8SDK newSdk = new A8SDK(_deviceIp); - // 添加心跳检测重试机制,适应网络不稳定情况 + // 保存到字段前进行验证,避免无效实例 bool isConnected = false; int maxHeartbeatRetries = 3; // 心跳检测最大重试次数 @@ -2159,12 +2471,13 @@ namespace JoyD.Windows.CS.Toprie // 添加延时,给SDK实例初始化一些时间 if (retry > 0) { - Console.WriteLine("等待500ms后重试..."); - Thread.Sleep(500); + int retryDelay = 500 + (retry - 1) * 300; // 递增延时 + Console.WriteLine($"等待{retryDelay}ms后重试..."); + Thread.Sleep(retryDelay); } // 发送心跳包验证连接 - int heartbeatResult = _a8Sdk.Heartbeat(); + int heartbeatResult = newSdk.Heartbeat(); if (heartbeatResult > 0) { Console.WriteLine("心跳检测成功!"); @@ -2173,12 +2486,12 @@ namespace JoyD.Windows.CS.Toprie } else { - Console.WriteLine("心跳检测失败"); + Console.WriteLine($"心跳检测失败,返回值: {heartbeatResult}"); } } catch (Exception ex) { - Console.WriteLine($"心跳检测异常: {ex.Message}"); + Console.WriteLine($"心跳检测异常: {ex.Message},堆栈: {ex.StackTrace}"); } } @@ -2189,9 +2502,11 @@ namespace JoyD.Windows.CS.Toprie { Console.WriteLine("进行额外连接验证..."); // 再次发送心跳包确保连接稳定 - int finalResult = _a8Sdk.Heartbeat(); + int finalResult = newSdk.Heartbeat(); if (finalResult > 0) { + // 验证成功后,安全地更新SDK实例 + Interlocked.Exchange(ref _a8Sdk, newSdk); Console.WriteLine($"使用IP地址 {_deviceIp} 重连成功"); _currentDeviceId = 1; // 临时ID,确保状态更新正确 UpdateConnectionStatus(ConnectionStatus.Connected, $"设备 {_deviceIp} 连接成功"); @@ -2200,7 +2515,7 @@ namespace JoyD.Windows.CS.Toprie } else { - Console.WriteLine("最终验证失败"); + Console.WriteLine($"最终验证失败,返回值: {finalResult}"); } } catch (Exception ex) @@ -2213,7 +2528,8 @@ namespace JoyD.Windows.CS.Toprie if (!connectionSuccessful) { Console.WriteLine($"使用IP地址 {_deviceIp} 重连失败,所有心跳尝试都未成功"); - _a8Sdk = null; + // 确保资源被释放 + try { newSdk = null; } catch { } } } else @@ -2223,12 +2539,13 @@ namespace JoyD.Windows.CS.Toprie } catch (Exception ex) { - Console.WriteLine($"使用IP地址重连异常: {ex.Message}"); + Console.WriteLine($"使用IP地址重连异常: {ex.Message},堆栈: {ex.StackTrace}"); } } // 如果IP地址连接失败,再尝试使用设备ID连接(保持兼容性) - if (!connectionSuccessful && _currentDeviceId != -1) + // 但如果已经在连接中,则跳过 + if (!connectionSuccessful && _currentDeviceId != -1 && !_isConnecting) { Console.WriteLine($"尝试使用设备ID {_currentDeviceId} 连接"); ConnectDevice(_currentDeviceId); @@ -2239,7 +2556,8 @@ namespace JoyD.Windows.CS.Toprie } // 如果没有保存的设备ID,但有搜索到的设备,尝试连接第一个 - if (!connectionSuccessful && _deviceIds != null && _deviceIds.Count > 0) + // 但如果已经在连接中,则跳过 + if (!connectionSuccessful && _deviceIds != null && _deviceIds.Count > 0 && !_isConnecting) { Console.WriteLine($"尝试使用搜索到的设备列表中的第一个设备"); if (int.TryParse(_deviceIds[0], out int deviceId)) @@ -2263,30 +2581,48 @@ namespace JoyD.Windows.CS.Toprie // 当所有连接尝试都失败时,明确更新状态为Disconnected UpdateConnectionStatus(ConnectionStatus.Disconnected, "所有连接尝试失败"); - // 设置下一次重连的时间间隔 - int retryInterval = _reconnectInterval; - if (_currentReconnectAttempt % 5 == 0) // 每5次尝试后增加间隔 - { - retryInterval = Math.Min(retryInterval * 2, 10000); // 最大10秒 - Console.WriteLine($"调整重连间隔为 {retryInterval}ms"); - } - Console.WriteLine($"重连失败,{retryInterval}ms后再次尝试"); + // 改进的间隔递增策略,避免频繁重连导致的网络压力 + int retryInterval = Math.Min(_reconnectInterval * (1 + _currentReconnectAttempt / 5), 20000); // 最大20秒 + Console.WriteLine($"调整重连间隔为 {retryInterval}ms"); - // 更新定时器,确保下一次重连 - if (_reconnectTimer != null) + // 安全地更新定时器 + System.Threading.Timer currentTimer = _reconnectTimer; + if (currentTimer != null) { - _reconnectTimer.Change(retryInterval, Timeout.Infinite); + try + { + // 使用一次性调度而非重复间隔 + currentTimer.Change(retryInterval, Timeout.Infinite); + } + catch (ObjectDisposedException) + { + Console.WriteLine("定时器已被释放,无法更新"); + } } } catch (Exception ex) { - Console.WriteLine($"重连回调函数发生异常: {ex.Message}"); + Console.WriteLine($"重连回调函数发生异常: {ex.Message},堆栈: {ex.StackTrace}"); // 确保定时器继续工作,防止重连机制中断 - if (_reconnectTimer != null) + System.Threading.Timer currentTimer = _reconnectTimer; + if (currentTimer != null) { - _reconnectTimer.Change(_reconnectInterval, Timeout.Infinite); + try + { + // 使用一次性调度而非重复间隔 + currentTimer.Change(_reconnectInterval, Timeout.Infinite); + } + catch (ObjectDisposedException) + { + Console.WriteLine("定时器已被释放,无法恢复"); + } } } + finally + { + // 无论如何都要重置重入标志,确保后续重连可以正常触发 + _isReconnecting = 0; + } } @@ -2307,10 +2643,23 @@ namespace JoyD.Windows.CS.Toprie /// private void StopHeartbeat() { - if (_heartbeatTimer != null) + try { - _heartbeatTimer.Change(Timeout.Infinite, Timeout.Infinite); - _heartbeatTimer.Dispose(); + // 使用Interlocked.Exchange确保线程安全 + System.Threading.Timer timerToDispose = Interlocked.Exchange(ref _heartbeatTimer, null); + if (timerToDispose != null) + { + // 立即停止定时器,避免回调执行 + timerToDispose.Change(Timeout.Infinite, Timeout.Infinite); + // 安全释放定时器资源 + timerToDispose.Dispose(); + Console.WriteLine("心跳检测已停止"); + } + } + catch (Exception ex) + { + Console.WriteLine($"停止心跳检测异常: {ex.Message}"); + // 确保即使异常,引用也被清空 _heartbeatTimer = null; } } @@ -2323,8 +2672,27 @@ namespace JoyD.Windows.CS.Toprie { try { - // 检查设备是否可达 - if (!PingDevice(_deviceIp)) + // 检查设备是否可达,使用改进的PingDevice方法 + bool deviceReachable = false; + int pingRetries = 2; + + // 添加ping重试机制 + for (int i = 0; i <= pingRetries; i++) + { + if (PingDevice(_deviceIp)) + { + deviceReachable = true; + break; + } + + if (i < pingRetries) + { + Console.WriteLine($"Ping设备失败,{300 * (i + 1)}ms后重试..."); + Thread.Sleep(300 * (i + 1)); // 递增延时 + } + } + + if (!deviceReachable) { Console.WriteLine("心跳检测失败,设备不可达"); UpdateConnectionStatus(ConnectionStatus.Disconnected, "心跳检测失败,设备不可达"); @@ -2337,13 +2705,56 @@ namespace JoyD.Windows.CS.Toprie return; } - // 使用SDK的Heartbeat方法进行心跳检测 - if (_a8Sdk != null) + // 使用SDK的Heartbeat方法进行心跳检测,添加重试机制 + A8SDK currentSdk = _a8Sdk; + if (currentSdk != null) { - if (_a8Sdk.Heartbeat() <= 0) + bool heartbeatSuccessful = false; + int heartbeatRetries = 1; + + for (int i = 0; i <= heartbeatRetries; i++) { - Console.WriteLine("SDK心跳检测失败"); - UpdateConnectionStatus(ConnectionStatus.Disconnected, "SDK心跳检测失败"); + try + { + int heartbeatResult = currentSdk.Heartbeat(); + if (heartbeatResult > 0) + { + heartbeatSuccessful = true; + Console.WriteLine("心跳检测成功"); + // 定期更新连接状态,表明连接正常 + if (_connectionStatus != ConnectionStatus.Connected) + { + UpdateConnectionStatus(ConnectionStatus.Connected, "设备连接已恢复"); + } + break; + } + else + { + Console.WriteLine($"SDK心跳检测失败,返回值: {heartbeatResult}"); + + if (i < heartbeatRetries) + { + Console.WriteLine("300ms后重试心跳检测..."); + Thread.Sleep(300); + } + } + } + catch (Exception ex) + { + Console.WriteLine($"心跳检测异常: {ex.Message}"); + + if (i < heartbeatRetries) + { + Console.WriteLine("300ms后重试心跳检测..."); + Thread.Sleep(300); + } + } + } + + if (!heartbeatSuccessful) + { + Console.WriteLine("SDK心跳检测失败,连接可能已断开"); + UpdateConnectionStatus(ConnectionStatus.Disconnected, "SDK心跳检测失败,连接已断开"); // 如果启用了自动重连,开始重连 if (_isAutoReconnectEnabled) @@ -2354,13 +2765,25 @@ namespace JoyD.Windows.CS.Toprie } else { - Console.WriteLine("SDK实例不存在"); + Console.WriteLine("SDK实例不存在,尝试重连"); + UpdateConnectionStatus(ConnectionStatus.Disconnected, "SDK实例不存在"); + + if (_isAutoReconnectEnabled) + { + StartAutoReconnect(); + } } } catch (Exception ex) { - Console.WriteLine($"心跳检测时发生异常: {ex.Message}"); + Console.WriteLine($"心跳检测时发生异常: {ex.Message},堆栈: {ex.StackTrace}"); OnConnectionException(new ConnectionExceptionEventArgs(ex, "心跳检测失败")); + + // 异常情况下也尝试重连 + if (_isAutoReconnectEnabled) + { + StartAutoReconnect(); + } } } @@ -2393,62 +2816,7 @@ namespace JoyD.Windows.CS.Toprie StopConnectionCheck(); } - /// - /// 检查系统网络连接是否可用 - /// - /// 网络是否可用 - private bool IsNetworkAvailable() - { - try - { - // 检查系统网络连接状态 - System.Net.NetworkInformation.NetworkInterface[] interfaces = - System.Net.NetworkInformation.NetworkInterface.GetAllNetworkInterfaces(); - - // 检查是否有活动的以太网或WiFi连接 - foreach (var ni in interfaces) - { - // 仅检查物理网络接口(以太网、WiFi等) - if (ni.OperationalStatus == System.Net.NetworkInformation.OperationalStatus.Up && - (ni.NetworkInterfaceType == System.Net.NetworkInformation.NetworkInterfaceType.Ethernet || - ni.NetworkInterfaceType == System.Net.NetworkInformation.NetworkInterfaceType.Wireless80211) && - ni.Name != "本地连接*" && !ni.Description.Contains("虚拟") && !ni.Description.Contains("Virtual")) - { - // 获取该网络接口的IP信息,检查是否有有效的IP地址 - var properties = ni.GetIPProperties(); - foreach (var addr in properties.UnicastAddresses) - { - // 检查是否有IPv4地址 - if (addr.Address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork) - { - // 如果设备IP不为空,可以尝试检查设备IP是否在该接口的子网内 - if (!string.IsNullOrEmpty(_deviceIp)) - { - try - { - // 检查设备IP是否与当前网络接口在同一子网(粗略判断) - var deviceAddress = System.Net.IPAddress.Parse(_deviceIp); - if (deviceAddress.GetAddressBytes()[0] == addr.Address.GetAddressBytes()[0]) - { - return true; - } - } - catch { } - } - return true; - } - } - } - } - return false; - } - catch (Exception ex) - { - Console.WriteLine($"检查网络可用性异常: {ex.Message}"); - // 发生异常时默认为网络不可用,避免误判 - return false; - } - } + /// /// 尝试ping设备IP地址 diff --git a/Windows/CS/Framework4.0/Toprie/Toprie/V8.cs b/Windows/CS/Framework4.0/Toprie/Toprie/V8.cs index 8783cb7..090ddea 100644 --- a/Windows/CS/Framework4.0/Toprie/Toprie/V8.cs +++ b/Windows/CS/Framework4.0/Toprie/Toprie/V8.cs @@ -14,7 +14,7 @@ namespace JoyD.Windows.CS.Toprie // 结构体引用已移至SharedStructures类 // 常量定义 - private const int SDK_PORT = 5000; + private const int SDK_PORT = 8080; private const int BUFFER_SIZE = 4096; private const int TIMEOUT = 3000; private const string CMD_HEAD = "+CMD"; @@ -1655,27 +1655,81 @@ namespace JoyD.Windows.CS.Toprie } } + // 使用UDP协议发送心跳命令,与SDK保持一致 public int Heartbeat() { + // 关键修改:与SDK保持一致,使用UDP协议发送心跳命令 + // SDK中使用的是UDP协议(socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) + // 之前的实现使用了TCP协议,这是心跳失败的根本原因 + // 端口修改为18890,与SDK中的CMD_SERVER_UDP_PORT保持一致 try { - // 根据SDK格式构建心跳命令 - SDK格式 - string command = $"{deviceIp}:{(int)CMD_TYPE.HEARTBEAT}$"; + // 根据SDK实际实现,心跳命令格式是 +CMD:24$ + string command = $"{CMD_HEAD}:{(int)CMD_TYPE.HEARTBEAT}$"; + byte[] commandBytes = Encoding.ASCII.GetBytes(command); - if (SendCommand(command, out string response)) + // SDK实现中会尝试3次心跳,这里也采用相同的策略 + for (int retry = 0; retry < 3; retry++) { - // 验证响应格式 - if (response != null) + Console.WriteLine($"心跳检测...(尝试 {retry + 1}/3)"); + Console.WriteLine($"心跳命令: {command}"); + Console.WriteLine($"目标IP: {deviceIp}:18890"); + + using (UdpClient udpClient = new UdpClient()) { - Console.WriteLine("心跳成功"); - return 1; // 返回1表示成功,与DeviceManager中的判断一致 + try + { + // 设置接收超时,与SDK中的50毫秒保持一致 + udpClient.Client.ReceiveTimeout = 50; + + // 发送UDP数据报,端口使用18890与SDK保持一致 + udpClient.Send(commandBytes, commandBytes.Length, deviceIp, 18890); + Console.WriteLine("UDP心跳命令已发送"); + + // 尝试接收响应 + IPEndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, 0); + byte[] responseBytes = udpClient.Receive(ref remoteEndPoint); + string response = Encoding.ASCII.GetString(responseBytes); + + Console.WriteLine($"收到UDP心跳响应: {response}"); + + // SDK要求响应中必须包含 ":ok" 字符串才算成功 + if (!string.IsNullOrEmpty(response) && response.Contains(":ok")) + { + Console.WriteLine("心跳成功: 响应包含':ok'"); + return 1; // 返回1表示成功,与DeviceManager中的判断一致 + } + else + { + Console.WriteLine("心跳响应不包含':ok',验证失败"); + } + } + catch (SocketException ex) when (ex.SocketErrorCode == SocketError.TimedOut) + { + // 超时异常,继续重试 + Console.WriteLine("UDP心跳接收超时"); + } + catch (Exception ex) + { + Console.WriteLine($"UDP心跳异常: {ex.Message}"); + } + } + + // 如果不是最后一次尝试,短暂延迟后重试 + if (retry < 2) + { + Thread.Sleep(100); } } + + // 3次尝试都失败 + Console.WriteLine("三次心跳检测均失败"); return -1; } catch (Exception ex) { Console.WriteLine($"心跳检测异常: {ex.Message}"); + Console.WriteLine($"异常堆栈: {ex.StackTrace}"); return -1; } }