mirror of
https://github.com/chaos-zhu/easynode.git
synced 2026-10-06 14:23:19 +08:00
feat: native 重连机制优化
This commit is contained in:
@@ -50,8 +50,15 @@ class SshTerminalController {
|
|||||||
try {
|
try {
|
||||||
final transport = await _transportFactory.open(
|
final transport = await _transportFactory.open(
|
||||||
config,
|
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;
|
_transport = transport;
|
||||||
terminal.write('[Info] 准备连接目标终端: ${config.name} - ${config.host}\r\n');
|
terminal.write('[Info] 准备连接目标终端: ${config.name} - ${config.host}\r\n');
|
||||||
// dartssh2 `SSHKeyPair.fromPem` already returns `List<SSHKeyPair>`, no
|
// dartssh2 `SSHKeyPair.fromPem` already returns `List<SSHKeyPair>`, no
|
||||||
@@ -68,18 +75,24 @@ class SshTerminalController {
|
|||||||
identities: identities,
|
identities: identities,
|
||||||
);
|
);
|
||||||
} on SshTransportException catch (error) {
|
} on SshTransportException catch (error) {
|
||||||
|
if (_disposed) return;
|
||||||
terminal.write('[Error] ${error.message}\r\n');
|
terminal.write('[Error] ${error.message}\r\n');
|
||||||
rethrow;
|
rethrow;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
if (_disposed) return;
|
||||||
terminal.write('[Error] $error\r\n');
|
terminal.write('[Error] $error\r\n');
|
||||||
rethrow;
|
rethrow;
|
||||||
}
|
}
|
||||||
|
if (_disposed) return;
|
||||||
final session = await _client!.shell();
|
final session = await _client!.shell();
|
||||||
|
if (_disposed) return;
|
||||||
_session = session;
|
_session = session;
|
||||||
_stdoutSub = session.stdout.listen((data) {
|
_stdoutSub = session.stdout.listen((data) {
|
||||||
|
if (_disposed) return;
|
||||||
_writeTerminalOutput(utf8.decode(data, allowMalformed: true));
|
_writeTerminalOutput(utf8.decode(data, allowMalformed: true));
|
||||||
});
|
});
|
||||||
_stderrSub = session.stderr.listen((data) {
|
_stderrSub = session.stderr.listen((data) {
|
||||||
|
if (_disposed) return;
|
||||||
_writeTerminalOutput(utf8.decode(data, allowMalformed: true));
|
_writeTerminalOutput(utf8.decode(data, allowMalformed: true));
|
||||||
});
|
});
|
||||||
terminal.onOutput = (data) {
|
terminal.onOutput = (data) {
|
||||||
@@ -140,6 +153,8 @@ class SshTerminalController {
|
|||||||
|
|
||||||
Future<void> disconnect() async {
|
Future<void> disconnect() async {
|
||||||
_disposed = true;
|
_disposed = true;
|
||||||
|
terminal.onOutput = null;
|
||||||
|
terminal.onResize = null;
|
||||||
await _stdoutSub?.cancel();
|
await _stdoutSub?.cancel();
|
||||||
await _stderrSub?.cancel();
|
await _stderrSub?.cancel();
|
||||||
_stdoutSub = null;
|
_stdoutSub = null;
|
||||||
|
|||||||
@@ -74,12 +74,18 @@ class TerminalSessionManager extends ChangeNotifier {
|
|||||||
final session = _findOrNull(id);
|
final session = _findOrNull(id);
|
||||||
if (session == null) return;
|
if (session == null) return;
|
||||||
final terminal = session.controller.terminal;
|
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(
|
session.controller = SshTerminalController(
|
||||||
config: session.config,
|
config: session.config,
|
||||||
terminal: terminal,
|
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);
|
await _connect(session);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -112,12 +118,18 @@ class TerminalSessionManager extends ChangeNotifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _connect(TerminalSession session) async {
|
Future<void> _connect(TerminalSession session) async {
|
||||||
|
final controller = session.controller;
|
||||||
session.status = TerminalSessionStatus.connecting;
|
session.status = TerminalSessionStatus.connecting;
|
||||||
session.lastError = null;
|
session.lastError = null;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
try {
|
try {
|
||||||
await session.controller.connect();
|
await controller.connect();
|
||||||
if (!_sessions.contains(session)) return;
|
// 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;
|
session.status = TerminalSessionStatus.connected;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
if (_shouldAutoStartStatusMonitor()) {
|
if (_shouldAutoStartStatusMonitor()) {
|
||||||
@@ -129,7 +141,10 @@ class TerminalSessionManager extends ChangeNotifier {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (!_sessions.contains(session)) return;
|
if (!_sessions.contains(session) ||
|
||||||
|
session.controller != controller) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
session.status = TerminalSessionStatus.error;
|
session.status = TerminalSessionStatus.error;
|
||||||
session.lastError = error.toString();
|
session.lastError = error.toString();
|
||||||
session.controller.terminal.write('\r\n[Error] ${session.lastError}\r\n');
|
session.controller.terminal.write('\r\n[Error] ${session.lastError}\r\n');
|
||||||
|
|||||||
Reference in New Issue
Block a user