feat: native 重连机制优化

This commit is contained in:
chaoszhu
2026-07-04 20:15:07 +08:00
parent c08513b91b
commit d16710501d
2 changed files with 36 additions and 6 deletions
@@ -50,8 +50,15 @@ class SshTerminalController {
try {
final transport = await _transportFactory.open(
config,
logger: (message) => terminal.write('[Info] $message\r\n'),
logger: (message) {
if (_disposed) return;
terminal.write('[Info] $message\r\n');
},
);
if (_disposed) {
await transport.close();
return;
}
_transport = transport;
terminal.write('[Info] 准备连接目标终端: ${config.name} - ${config.host}\r\n');
// dartssh2 `SSHKeyPair.fromPem` already returns `List<SSHKeyPair>`, no
@@ -68,18 +75,24 @@ class SshTerminalController {
identities: identities,
);
} on SshTransportException catch (error) {
if (_disposed) return;
terminal.write('[Error] ${error.message}\r\n');
rethrow;
} catch (error) {
if (_disposed) return;
terminal.write('[Error] $error\r\n');
rethrow;
}
if (_disposed) return;
final session = await _client!.shell();
if (_disposed) return;
_session = session;
_stdoutSub = session.stdout.listen((data) {
if (_disposed) return;
_writeTerminalOutput(utf8.decode(data, allowMalformed: true));
});
_stderrSub = session.stderr.listen((data) {
if (_disposed) return;
_writeTerminalOutput(utf8.decode(data, allowMalformed: true));
});
terminal.onOutput = (data) {
@@ -140,6 +153,8 @@ class SshTerminalController {
Future<void> disconnect() async {
_disposed = true;
terminal.onOutput = null;
terminal.onResize = null;
await _stdoutSub?.cancel();
await _stderrSub?.cancel();
_stdoutSub = null;
@@ -74,12 +74,18 @@ class TerminalSessionManager extends ChangeNotifier {
final session = _findOrNull(id);
if (session == null) return;
final terminal = session.controller.terminal;
await session.controller.disconnect();
// Force-disconnect regardless of current state (connecting/connected/error).
final oldController = session.controller;
session.controller = SshTerminalController(
config: session.config,
terminal: terminal,
);
session.controller.terminal.write('\r\n[Reconnecting]\r\n');
session.status = TerminalSessionStatus.connecting;
notifyListeners();
// Tear down the old controller in the background — it no longer owns the
// terminal so its disconnect cannot interfere with the new connection.
unawaited(oldController.disconnect());
terminal.write('\r\n[Reconnecting]\r\n');
await _connect(session);
}
@@ -112,12 +118,18 @@ class TerminalSessionManager extends ChangeNotifier {
}
Future<void> _connect(TerminalSession session) async {
final controller = session.controller;
session.status = TerminalSessionStatus.connecting;
session.lastError = null;
notifyListeners();
try {
await session.controller.connect();
if (!_sessions.contains(session)) return;
await controller.connect();
// After await, verify this controller is still the active one — a
// concurrent reconnect may have replaced it.
if (!_sessions.contains(session) ||
session.controller != controller) {
return;
}
session.status = TerminalSessionStatus.connected;
notifyListeners();
if (_shouldAutoStartStatusMonitor()) {
@@ -129,7 +141,10 @@ class TerminalSessionManager extends ChangeNotifier {
);
}
} catch (error) {
if (!_sessions.contains(session)) return;
if (!_sessions.contains(session) ||
session.controller != controller) {
return;
}
session.status = TerminalSessionStatus.error;
session.lastError = error.toString();
session.controller.terminal.write('\r\n[Error] ${session.lastError}\r\n');