From 7feb78e834ba9662d96c9f07ed4cdd8d091c4133 Mon Sep 17 00:00:00 2001 From: Rain Yang Date: Sun, 12 Oct 2025 12:23:34 +0800 Subject: [PATCH] fix: Enhance connection robustness by rebuilding NWConnection on failure --- MeoAsstMac/Core/MaaToolClient.swift | 82 +++++++++++++++++------------ 1 file changed, 48 insertions(+), 34 deletions(-) diff --git a/MeoAsstMac/Core/MaaToolClient.swift b/MeoAsstMac/Core/MaaToolClient.swift index 189a941e7..c853ea9ee 100644 --- a/MeoAsstMac/Core/MaaToolClient.swift +++ b/MeoAsstMac/Core/MaaToolClient.swift @@ -9,7 +9,7 @@ import Foundation import Network actor MaaToolClient { - private let connection: NWConnection + private var connection: NWConnection init?(address: String) async { let parts = address.split(separator: ":") @@ -21,51 +21,65 @@ actor MaaToolClient { } let host = NWEndpoint.Host(String(parts[0])) - connection = NWConnection(host: host, port: port, using: .tcp) + var retryCount = 0 + let maxRetries = 20 - let states = AsyncStream { continuation in - connection.stateUpdateHandler = { state in - if state == .ready { - continuation.finish() - } else { - continuation.yield(state) + while retryCount < maxRetries { + connection = NWConnection(host: host, port: port, using: .tcp) + + let states = AsyncStream { continuation in + connection.stateUpdateHandler = { state in + continuation.yield(state) } - } - continuation.onTermination = { [weak self] reason in - guard reason == .cancelled else { return } - self?.connection.cancel() + continuation.onTermination = { [weak self] reason in + guard reason == .cancelled else { return } + Task { + await self?.cancelActorConnection() + } + } } - } - connection.start(queue: .global()) + connection.start(queue: .global()) - var retryCount = 0 - let maxRetries = 20 + state_enum: for await state in states { + switch state { + case .setup, .preparing, .cancelled: + break + case .waiting, .failed: + retryCount += 1 + if retryCount >= maxRetries { + connection.cancel() + break state_enum + } - for await state in states { - switch state { - case .setup, .preparing, .cancelled: - break - case .waiting: - try? await Task.sleep(for: .seconds(0.5)) - connection.restart() - case .failed: - retryCount += 1 - if retryCount > maxRetries { - connection.cancel() // 取消连接 - return nil // 达到最大重试次数,初始化失败 + try? await Task.sleep(for: .seconds(0.5)) + + if case .failed = state { + // 明确失败,清理旧连接,让外部循环创建新连接 + connection.cancel() + break state_enum + } else { + // .waiting 状态下,使用 restart() + connection.restart() + } + case .ready: + return + @unknown default: + fatalError() } - try? await Task.sleep(nanoseconds: 500_000_000) - connection.restart() - case .ready: - return - @unknown default: - fatalError() } } + return nil + } + + // MARK: - Private Methods + private func cancelActorConnection() { + connection.cancel() } + // MARK: - Public Methods + func terminate() async throws { guard connection.state == .ready else { return }