diff --git a/client/lib/state/connection_provider.dart b/client/lib/state/connection_provider.dart index 6eb2272..90ad956 100644 --- a/client/lib/state/connection_provider.dart +++ b/client/lib/state/connection_provider.dart @@ -216,12 +216,13 @@ class ConnectionController extends StateNotifier { _stopWatchdog(); case VpnStatus.off: case VpnStatus.error: - final wasActive = state.phase == VpnPhase.on || state.phase == VpnPhase.connecting; - // 路径 A:非用户主动、且本来在连/已连 → 节点数据面死导致的意外掉线。 - // 给原因(节点异常)+ 刷节点列表让服务端 down 浮现。_offNotice 让显式断开与本回调统一。 - if (!_userDisconnect && wasActive && _offNotice == null) { + // 路径 A:仅「真正连上过(on)再非用户主动掉线」才判节点异常(数据面死/REALITY 断)。 + // 必须排除 connecting 握手期的瞬态 off——macOS NE 连接序列是 off→connecting→off→ + // connecting→on,connecting 期的 off 是握手抖动、不是异常(否则一连接就误报「节点异常」)。 + final wasConnected = state.phase == VpnPhase.on; + if (!_userDisconnect && wasConnected && _offNotice == null) { _offNotice = _ref.read(appTextProvider).nodeUnhealthyError; - logLine('Watchdog', 'unexpected kernel ${s.name} while $wasActive → node interrupted'); + logLine('Watchdog', 'unexpected kernel ${s.name} after connected → node interrupted'); unawaited(_ref.read(nodesProvider.notifier).refresh()); } state = ConnectionState(phase: VpnPhase.off, error: _offNotice);