实现心跳检测

This commit is contained in:
zqm
2025-10-27 15:36:18 +08:00
parent 5a3d8dd975
commit f4aebca633
2 changed files with 584 additions and 162 deletions

View File

@@ -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;
/// <summary>
/// 检查网络是否可用
/// </summary>
/// <returns>网络是否可用</returns>
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,39 +315,130 @@ namespace JoyD.Windows.CS.Toprie
/// <returns>是否初始化成功</returns>
private bool Initialize()
{
// 双重检查锁定模式,确保线程安全的初始化
if (_isInitialized)
return true;
try
{
// 初始化设备ID列表
_deviceIds = new List<string>(); // 保持与类中定义一致
_reconnectTimer = null;
_heartbeatTimer = null;
_connectionCheckTimer = null;
// 清理旧资源(如果有)
CleanupResources();
// 初始化设备ID列表和相关变量
_deviceIds = new List<string>();
_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;
}
}
/// <summary>
/// 清理所有资源,确保安全释放
/// </summary>
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}");
}
}
/// <summary>
/// 尝试ping设备IP地址
/// </summary>
@@ -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
/// <summary>
/// 获取或设置设备IP地址
/// </summary>
public string IPAddress
{
get { return _deviceIp; }
set
{
if (!string.IsNullOrEmpty(value))
{
_deviceIp = value;
Console.WriteLine($"设备IP地址已设置为: {_deviceIp}");
}
}
}
/// <summary>
/// 当前图像模式
/// </summary>
@@ -1749,6 +1992,17 @@ namespace JoyD.Windows.CS.Toprie
return;
}
// 检查是否正在重连中,如果是则不执行连接
if (_isReconnecting != 0)
{
Console.WriteLine("检测到正在重连过程中,暂停设备连接");
UpdateConnectionStatus(ConnectionStatus.Disconnected, "正在进行自动重连,请稍后再试");
return;
}
// 设置连接中标志
_isConnecting = true;
// 停止任何现有的重连定时器
StopAutoReconnect();
@@ -1774,13 +2028,18 @@ namespace JoyD.Windows.CS.Toprie
}
// 使用真实SDK连接设备
Console.WriteLine($"正在使用SDK连接设备 {deviceId}IP地址: {_deviceIp}");
Console.WriteLine($"正在使用SDK连接设备 {deviceId}IP地址: {_deviceIp},端口: {_devicePort}");
// 先检测IP可达性避免不必要的SDK初始化
if (!PingDevice(_deviceIp))
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} 不可达");
throw new Exception($"设备IP {_deviceIp} 不可达,请检查网络连接");
}
// 创建SDK实例
@@ -1788,21 +2047,37 @@ namespace JoyD.Windows.CS.Toprie
Console.WriteLine("SDK实例创建完成");
// 验证连接是否成功(通过心跳检测)
if (_a8Sdk.Heartbeat() <= 0)
Console.WriteLine("尝试发送心跳包进行连接验证...(尝试 1/3)");
int heartbeatResult = _a8Sdk.Heartbeat();
// 尝试多次心跳检测,增加连接成功率
int retryCount = 1;
while (heartbeatResult <= 0 && retryCount < 3)
{
Console.WriteLine("心跳检测失败");
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("心跳检测失败");
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
/// <summary>
/// 开始自动重连
/// </summary>
// 重入保护标志
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);
}
/// <summary>
@@ -2104,10 +2386,44 @@ namespace JoyD.Windows.CS.Toprie
/// <param name="state">定时器状态</param>
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,29 +2581,47 @@ namespace JoyD.Windows.CS.Toprie
// 当所有连接尝试都失败时明确更新状态为Disconnected
UpdateConnectionStatus(ConnectionStatus.Disconnected, "所有连接尝试失败");
// 设置下一次重连的时间间隔
int retryInterval = _reconnectInterval;
if (_currentReconnectAttempt % 5 == 0) // 每5次尝试后增加间隔
{
retryInterval = Math.Min(retryInterval * 2, 10000); // 最大10秒
// 改进的间隔递增策略,避免频繁重连导致的网络压力
int retryInterval = Math.Min(_reconnectInterval * (1 + _currentReconnectAttempt / 5), 20000); // 最大20秒
Console.WriteLine($"调整重连间隔为 {retryInterval}ms");
}
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
/// </summary>
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();
}
/// <summary>
/// 检查系统网络连接是否可用
/// </summary>
/// <returns>网络是否可用</returns>
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;
}
}
/// <summary>
/// 尝试ping设备IP地址

View File

@@ -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实际实现心跳命令格式是 +CMD:24$
string command = $"{CMD_HEAD}:{(int)CMD_TYPE.HEARTBEAT}$";
byte[] commandBytes = Encoding.ASCII.GetBytes(command);
// SDK实现中会尝试3次心跳这里也采用相同的策略
for (int retry = 0; retry < 3; retry++)
{
Console.WriteLine($"心跳检测...(尝试 {retry + 1}/3)");
Console.WriteLine($"心跳命令: {command}");
Console.WriteLine($"目标IP: {deviceIp}:18890");
using (UdpClient udpClient = new UdpClient())
{
try
{
// 根据SDK格式构建心跳命令 - SDK格式
string command = $"{deviceIp}:{(int)CMD_TYPE.HEARTBEAT}$";
// 设置接收超时与SDK中的50毫秒保持一致
udpClient.Client.ReceiveTimeout = 50;
if (SendCommand(command, out string response))
// 发送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"))
{
// 验证响应格式
if (response != null)
{
Console.WriteLine("心跳成功");
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;
}
}