This commit is contained in:
lzx
2026-01-23 16:11:20 +08:00
parent e4e0e44555
commit efce651809
10 changed files with 276 additions and 77 deletions

View File

@@ -20,8 +20,13 @@ class ClientCore {
bool _isConnected = false;
DateTime? _lastPingTime;
Timer? _heartbeatTimer;
Timer? _reconnectTimer;
StreamController<String>? _logController;
// 重连相关
int _reconnectAttempts = 0;
static const int _maxReconnectAttempts = 5;
// 需要GPU和挖矿软件信息用于认证
Map<String, dynamic>? _gpusInfo;
List<String>? _miningSofts;
@@ -86,11 +91,10 @@ class ClientCore {
_socket = await Socket.connect(host, port, timeout: const Duration(seconds: 10));
_isConnected = true;
// 连接成功,重置重连计数器
_reconnectAttempts = 0;
_log('连接到服务器成功: $_serverUrl');
// 注意:不在这里发送身份认证,等待机器码获取完成后再发送
// 身份认证消息将在机器码获取完成后通过 sendMachineCode() 发送
// 开始接收消息
_socket!.listen(
_onDataReceived,
@@ -100,6 +104,12 @@ class ClientCore {
);
onStatusChanged?.call(ui.ClientStatus.online);
// 如果已经有机器码和认证信息,立即发送认证消息(重连场景)
if (_auth != null && _machineCode != null && _machineCode!.isNotEmpty && _machineCode != '正在获取...' && _machineCode != '获取失败') {
_log('重连成功,自动发送身份认证消息');
_sendMachineCode();
}
} catch (e) {
_isConnected = false;
_log('连接失败: $e');
@@ -280,20 +290,54 @@ class ClientCore {
_reconnect();
}
/// 重连
/// 重连(指数退避策略)
void _reconnect() {
Future.delayed(const Duration(seconds: 5), () {
// 检查是否正在停止或已停止
// 取消之前的重连定时器
_reconnectTimer?.cancel();
// 检查是否正在停止或已停止
if (_socket == null || _logController == null) {
return; // 已经停止,不执行重连
}
// 检查是否已达到最大重试次数
if (_reconnectAttempts >= _maxReconnectAttempts) {
_log('已达到最大重连次数($_maxReconnectAttempts次),停止重连');
return;
}
// 计算延迟时间10秒 * 2^(重试次数)
// 第1次10秒第2次20秒第3次40秒第4次80秒第5次160秒
final delaySeconds = 10 * (1 << _reconnectAttempts);
_reconnectAttempts++;
_log('将在 ${delaySeconds}秒 后进行第 $_reconnectAttempts 次重连尝试(最多$_maxReconnectAttempts次');
_reconnectTimer = Timer(Duration(seconds: delaySeconds), () {
// 再次检查是否正在停止或已停止
if (_socket == null || _logController == null) {
return; // 已经停止,不执行重连
}
if (!_isConnected) {
_log('尝试重新连接...');
_connect().catchError((e) {
_log('重连失败: $e');
});
// 检查是否已经连接成功(可能在其他地方已经连接)
if (_isConnected) {
_reconnectAttempts = 0; // 重置计数器
return;
}
_log('尝试第 $_reconnectAttempts 次重新连接...');
_connect().then((_) {
// 连接成功,计数器已在 _connect() 中重置
_log('重连成功');
}).catchError((e) {
_log('$_reconnectAttempts 次重连失败: $e');
// 如果未达到最大重试次数,继续重连
if (_reconnectAttempts < _maxReconnectAttempts) {
_reconnect();
} else {
_log('已达到最大重连次数,停止重连');
}
});
});
}
@@ -307,6 +351,8 @@ class ClientCore {
_log('超过60分钟未收到心跳连接可能已断开');
_isConnected = false;
onStatusChanged?.call(ui.ClientStatus.offline);
// 心跳超时视为新的断开事件,重置重连计数器
_reconnectAttempts = 0;
_reconnect();
}
}
@@ -331,6 +377,10 @@ class ClientCore {
_heartbeatTimer?.cancel();
_heartbeatTimer = null;
_reconnectTimer?.cancel();
_reconnectTimer = null;
_reconnectAttempts = 0; // 重置重连计数器
// 先取消 socket 监听,避免 onDone 回调在关闭 controller 后执行
_socket?.destroy();
_socket = null;

View File

@@ -15,6 +15,10 @@ class MiningManager {
MiningTaskInfo? _currentTask;
Timer? _taskMonitor;
final StreamController<String> _minerLogController = StreamController<String>.broadcast();
final StreamController<void> _processExitController = StreamController<void>.broadcast();
/// 进程退出事件流
Stream<void> get processExitStream => _processExitController.stream;
/// 启动挖矿
Future<bool> startMining(MiningTaskInfo task, MiningConfig config) async {
@@ -71,6 +75,7 @@ class MiningManager {
_currentTask = task;
_startTaskMonitor(task);
_startLogCapture();
_monitorProcessExit();
_logger.info('挖矿已启动 (PID: ${_currentProcess!.pid})');
return true;
@@ -183,6 +188,28 @@ class MiningManager {
});
}
/// 监控进程退出
void _monitorProcessExit() {
if (_currentProcess == null) return;
_currentProcess!.exitCode.then((exitCode) {
_logger.warning('挖矿进程已退出,退出码: $exitCode');
// 清理状态
_currentProcess = null;
final wasMining = _currentTask != null;
_currentTask = null;
_taskMonitor?.cancel();
_taskMonitor = null;
// 发送进程退出事件
if (wasMining) {
_processExitController.add(null);
}
}).catchError((e) {
_logger.severe('监控进程退出失败: $e');
});
}
/// 获取挖矿日志流
Stream<String> get minerLogStream => _minerLogController.stream;
@@ -192,6 +219,7 @@ class MiningManager {
/// 清理资源
void dispose() {
_minerLogController.close();
_processExitController.close();
}
}

View File

@@ -19,6 +19,7 @@ class SustainMiner {
SustainMiningConfig? _config;
MiningConfig? _miningConfig;
Timer? _monitorTimer;
StreamSubscription<void>? _processExitSubscription;
bool _isRunning = false;
bool _isPaused = false;
@@ -104,6 +105,7 @@ class SustainMiner {
_logger.info('启动持续挖矿...');
await _startMining();
_startProcessMonitor();
}
/// 停止持续挖矿
@@ -111,6 +113,8 @@ class SustainMiner {
_isRunning = false;
_monitorTimer?.cancel();
_monitorTimer = null;
_processExitSubscription?.cancel();
_processExitSubscription = null;
if (_miningManager.isMining && _miningManager.currentTask == null) {
// 只有持续挖矿任务在运行时才停止
@@ -148,6 +152,10 @@ class SustainMiner {
_logger.info('恢复持续挖矿...');
_isPaused = false;
await _startMining();
// 确保进程监控已启动
if (_processExitSubscription == null) {
_startProcessMonitor();
}
}
/// 启动挖矿
@@ -176,6 +184,26 @@ class SustainMiner {
await _miningManager.startMining(task, _miningConfig!);
}
/// 启动进程监控
void _startProcessMonitor() {
_processExitSubscription?.cancel();
// 监听挖矿进程退出事件
_processExitSubscription = _miningManager.processExitStream.listen((_) {
// 检查是否是持续挖矿进程退出
if (_isRunning && !_isPaused && !_miningManager.isMining && _miningManager.currentTask == null) {
_logger.warning('持续挖矿进程意外退出将在5秒后自动重启...');
// 延迟5秒后重启避免频繁重启
Future.delayed(const Duration(seconds: 5), () {
if (_isRunning && !_isPaused && !_miningManager.isMining) {
_logger.info('自动重启持续挖矿...');
_startMining();
}
});
}
});
}
String _trimQuotes(String value) {
var v = value.trim();
if (v.length >= 2) {