using System;
using System.Threading;
using System.Threading.Tasks;
using Serilog;
namespace Protocol376Simulator.Services
{
///
/// 重连服务类,负责处理自动重连逻辑
///
public class ReconnectionService
{
private readonly NetworkService _networkService;
private readonly string _deviceIdentifier;
private CancellationTokenSource _reconnectCancellationTokenSource;
private Task _reconnectTask;
private readonly object _reconnectLock = new object();
// 重连配置
private bool _autoReconnect = true;
private int _reconnectAttempts = 0;
private int _maxReconnectAttempts = 5;
private TimeSpan _reconnectDelay = TimeSpan.FromSeconds(5);
private TimeSpan _reconnectIncreaseDelay = TimeSpan.FromSeconds(5);
private DateTime _lastReconnectTime = DateTime.MinValue;
private TimeSpan _minReconnectInterval = TimeSpan.FromSeconds(30);
private bool _isReconnecting = false;
///
/// 当重连尝试开始时触发
///
public event EventHandler ReconnectAttemptStarted;
///
/// 当重连尝试结束时触发
///
public event EventHandler ReconnectAttemptCompleted;
///
/// 是否启用自动重连
///
public bool AutoReconnect
{
get => _autoReconnect;
set => _autoReconnect = value;
}
///
/// 重连尝试次数
///
public int ReconnectAttempts => _reconnectAttempts;
///
/// 是否正在重连
///
public bool IsReconnecting => _isReconnecting;
///
/// 构造函数
///
/// 网络服务
/// 设备标识(用于日志)
public ReconnectionService(NetworkService networkService, string deviceIdentifier)
{
_networkService = networkService ?? throw new ArgumentNullException(nameof(networkService));
_deviceIdentifier = deviceIdentifier;
// 订阅网络服务的连接状态变更事件
_networkService.ConnectionStatusChanged += OnConnectionStatusChanged;
}
///
/// 设置重连参数
///
/// 是否启用自动重连
/// 最大重连尝试次数
/// 重连延迟(秒)
public void SetReconnectParameters(bool autoReconnect, int maxAttempts, int delaySeconds)
{
_autoReconnect = autoReconnect;
_maxReconnectAttempts = maxAttempts;
_reconnectDelay = TimeSpan.FromSeconds(delaySeconds);
_reconnectIncreaseDelay = TimeSpan.FromSeconds(delaySeconds);
Log.Information("{DeviceId} 已设置重连参数: 自动重连={AutoReconnect}, 最大尝试次数={MaxAttempts}, 延迟={Delay}秒",
_deviceIdentifier, _autoReconnect, _maxReconnectAttempts, delaySeconds);
}
///
/// 连接状态变更处理
///
private void OnConnectionStatusChanged(object sender, NetworkService.ConnectionStatus status)
{
if (status == NetworkService.ConnectionStatus.Failed ||
status == NetworkService.ConnectionStatus.Disconnected)
{
// 如果启用了自动重连,开始重连任务
if (_autoReconnect)
{
StartReconnectAsync();
}
}
else if (status == NetworkService.ConnectionStatus.Connected)
{
// 连接成功,重置重连尝试次数
_reconnectAttempts = 0;
StopReconnect();
}
}
///
/// 开始重连任务
///
private void StartReconnectAsync()
{
lock (_reconnectLock)
{
// 如果已经在重连中,不要重复启动
if (_isReconnecting)
{
return;
}
// 如果距离上次重连时间太短,不要立即重连
TimeSpan timeSinceLastReconnect = DateTime.Now - _lastReconnectTime;
if (timeSinceLastReconnect < _minReconnectInterval)
{
Log.Information("{DeviceId} 距离上次重连尝试时间太短,将等待 {WaitTime} 秒后再尝试",
_deviceIdentifier, (_minReconnectInterval - timeSinceLastReconnect).TotalSeconds);
// 等待一段时间后再次检查是否需要重连
Task.Delay(_minReconnectInterval - timeSinceLastReconnect)
.ContinueWith(_ => StartReconnectAsync());
return;
}
_isReconnecting = true;
_reconnectCancellationTokenSource?.Cancel();
_reconnectCancellationTokenSource = new CancellationTokenSource();
_reconnectTask = ReconnectAsync(_reconnectCancellationTokenSource.Token);
}
}
///
/// 停止重连任务
///
private void StopReconnect()
{
lock (_reconnectLock)
{
_reconnectCancellationTokenSource?.Cancel();
_isReconnecting = false;
}
}
///
/// 重连任务实现
///
private async Task ReconnectAsync(CancellationToken cancellationToken)
{
// 记录开始重连的时间
_lastReconnectTime = DateTime.Now;
while (!cancellationToken.IsCancellationRequested)
{
_reconnectAttempts++;
// 检查是否超过最大尝试次数
if (_maxReconnectAttempts > 0 && _reconnectAttempts > _maxReconnectAttempts)
{
Log.Warning("{DeviceId} 已达到最大重连尝试次数 {MaxAttempts},停止重连",
_deviceIdentifier, _maxReconnectAttempts);
lock (_reconnectLock)
{
_isReconnecting = false;
}
ReconnectAttemptCompleted?.Invoke(this, false);
return;
}
Log.Information("{DeviceId} 尝试重连 (第 {Attempt}/{MaxAttempts} 次)...",
_deviceIdentifier, _reconnectAttempts, _maxReconnectAttempts > 0 ? _maxReconnectAttempts.ToString() : "∞");
// 触发重连开始事件
ReconnectAttemptStarted?.Invoke(this, _reconnectAttempts);
try
{
// 尝试重新连接
await _networkService.ConnectAsync();
// 如果连接成功,退出重连循环
Log.Information("{DeviceId} 重连成功", _deviceIdentifier);
lock (_reconnectLock)
{
_isReconnecting = false;
}
// 触发重连完成事件
ReconnectAttemptCompleted?.Invoke(this, true);
return;
}
catch (Exception ex)
{
Log.Warning("{DeviceId} 重连失败: {ErrorMessage}", _deviceIdentifier, ex.Message);
// 触发重连完成事件(失败)
ReconnectAttemptCompleted?.Invoke(this, false);
}
// 计算下一次重连的延迟时间(逐渐增加)
TimeSpan currentDelay = TimeSpan.FromMilliseconds(
_reconnectDelay.TotalMilliseconds +
(_reconnectAttempts - 1) * _reconnectIncreaseDelay.TotalMilliseconds);
Log.Information("{DeviceId} 将在 {Delay} 秒后重试...",
_deviceIdentifier, currentDelay.TotalSeconds);
// 等待一段时间后重试
try
{
await Task.Delay(currentDelay, cancellationToken);
}
catch (OperationCanceledException)
{
// 任务被取消,退出循环
break;
}
}
// 如果是因为取消而退出循环
if (cancellationToken.IsCancellationRequested)
{
lock (_reconnectLock)
{
_isReconnecting = false;
}
Log.Information("{DeviceId} 重连任务已取消", _deviceIdentifier);
}
}
///
/// 强制立即尝试重连
///
public async Task ForceReconnectAsync()
{
// 停止当前的重连任务
StopReconnect();
// 等待一段时间确保任务停止
await Task.Delay(100);
// 重置计数并开始新的重连
_reconnectAttempts = 0;
StartReconnectAsync();
}
}
}