diff --git a/Tun/Configuration/SDLConfiguration.swift b/Tun/Configuration/SDLConfiguration.swift index 7df78c2..437e457 100644 --- a/Tun/Configuration/SDLConfiguration.swift +++ b/Tun/Configuration/SDLConfiguration.swift @@ -146,7 +146,7 @@ extension SDLConfiguration { guard let networkAddress = parseNetworkAddress(networkAddressDict) else { return nil } - let acl = parseACL(options["acl"]) + let acl = parseACL(options["exposed_service"]) // 网络出口配置是可选的 var exitNode: ExitNode? = nil diff --git a/Tun/Super/SDLSuperClient.swift b/Tun/Super/SDLSuperClient.swift index bfb9b1d..e3e97fb 100644 --- a/Tun/Super/SDLSuperClient.swift +++ b/Tun/Super/SDLSuperClient.swift @@ -8,20 +8,15 @@ import Foundation import Network -actor SDLSuperClient { - enum State { - case idle - case running - case stopped - } - - private var state: State = .idle - +final class SDLSuperClient: @unchecked Sendable { private let queue = DispatchQueue(label: "com.sdl.SuperClient.queue") // 专用队列保证线程安全 - // 数据流 public let messageStream: AsyncThrowingStream private let messageCont: AsyncThrowingStream.Continuation + private let stateLock = NSLock() + private var isStarted = false + private var isStopped = false + private var isMessageContinuationFinished = false private let connection: NWConnection private let maxBufferSize: Int @@ -62,7 +57,7 @@ actor SDLSuperClient { } func run() async throws { - guard self.state == .idle else { + guard self.markStarted() else { return } @@ -114,25 +109,32 @@ actor SDLSuperClient { } private func runStateLoop(_ stateStream: AsyncThrowingStream) async throws { + do { + try await self.waitUntilReady(stateStream) + self.connection.stateUpdateHandler = nil + try await self.readLoop() + } catch is CancellationError { + throw CancellationError() + } catch let error as SDLSuperError { + self.finishMessageStream(throwing: error) + throw error + } catch { + let wrappedError = SDLSuperError.internalError(error) + self.finishMessageStream(throwing: wrappedError) + throw wrappedError + } + } + + private func waitUntilReady(_ stateStream: AsyncThrowingStream) async throws { for try await state in stateStream { try Task.checkCancellation() - switch state { - case .ready: - self.state = .running - try await self.readLoop() + if case .ready = state { return - case .failed(let error): - let wrappedError = SDLSuperError.connectionFailed(error) - self.messageCont.finish(throwing: wrappedError) - throw wrappedError - case .cancelled: - self.messageCont.finish(throwing: SDLSuperError.connectionCancelled) - throw SDLSuperError.connectionCancelled - default: - break } } + + throw SDLSuperError.connectionCancelled } private func readLoop() async throws { @@ -154,18 +156,18 @@ actor SDLSuperClient { } catch is CancellationError { throw CancellationError() } catch let error as SDLSuperError { - self.messageCont.finish(throwing: error) + self.finishMessageStream(throwing: error) throw error } catch { let wrappedError = SDLSuperError.internalError(error) - self.messageCont.finish(throwing: wrappedError) + self.finishMessageStream(throwing: wrappedError) throw wrappedError } } func send(type: SDLPacketType, data: Data) { - guard case .running = state, connection.state == .ready else { + guard connection.state == .ready else { return } @@ -176,10 +178,8 @@ actor SDLSuperClient { connection.send(content: packet, completion: .contentProcessed { [weak self] error in if let error { - Task { - SDLLogger.log("[SDLSuperClient] send data get error: \(error)", for: .debug) - self?.messageCont.finish(throwing: SDLSuperError.writeFailed(error)) - } + SDLLogger.log("[SDLSuperClient] send data get error: \(error)", for: .debug) + self?.finishMessageStream(throwing: SDLSuperError.writeFailed(error)) } }) } @@ -213,19 +213,64 @@ actor SDLSuperClient { } func stop() { - guard self.state != .stopped else { + guard self.markStopped() else { return } - - self.state = .stopped let connection = self.connection connection.stateUpdateHandler = nil connection.cancel() - self.messageCont.finish() + self.finishMessageStream() SDLLogger.log("[SDLSuperClient] stopped") } + + private func markStarted() -> Bool { + self.stateLock.lock() + defer { + self.stateLock.unlock() + } + + guard !self.isStarted, !self.isStopped else { + return false + } + + self.isStarted = true + return true + } + + private func markStopped() -> Bool { + self.stateLock.lock() + defer { + self.stateLock.unlock() + } + + guard !self.isStopped else { + return false + } + + self.isStopped = true + return true + } + + private func finishMessageStream(throwing error: Error? = nil) { + self.stateLock.lock() + let shouldFinish = !self.isMessageContinuationFinished + if shouldFinish { + self.isMessageContinuationFinished = true + } + self.stateLock.unlock() + + guard shouldFinish else { + return + } + + if let error { + self.messageCont.finish(throwing: error) + } else { + self.messageCont.finish() + } + } deinit { SDLLogger.log("[SDLSuperClient] deinit") diff --git a/Tun/Super/SDLSuperSession.swift b/Tun/Super/SDLSuperSession.swift index 9759988..87b4b15 100644 --- a/Tun/Super/SDLSuperSession.swift +++ b/Tun/Super/SDLSuperSession.swift @@ -32,11 +32,11 @@ final class SDLSuperSession: @unchecked Sendable { } func stop() async { - await self.client.stop() + self.client.stop() } func send(type: SDLPacketType, data: Data) async { - await self.client.send(type: type, data: data) + self.client.send(type: type, data: data) } private func runLoops() async throws { @@ -74,7 +74,7 @@ final class SDLSuperSession: @unchecked Sendable { while true { try await Task.sleep(for: .seconds(5)) try Task.checkCancellation() - await self.client.send(type: .ping, data: Data()) + self.client.send(type: .ping, data: Data()) } } } diff --git a/punchnet/App/AppContext.swift b/punchnet/App/AppContext.swift index 8207f14..edcca80 100644 --- a/punchnet/App/AppContext.swift +++ b/punchnet/App/AppContext.swift @@ -144,7 +144,7 @@ class AppContext { accessToken: session.accessToken, identityId: context.identityId, hostname: context.hostname, - acl: context.acl, + exposedService: context.exposedService, exitNodeIp: self.selectedExitNodeIp ) try await self.vpnManager.enableVpn(options: options) diff --git a/punchnet/Core/SystemConfig.swift b/punchnet/Core/SystemConfig.swift index 8ec433f..a8777ca 100644 --- a/punchnet/Core/SystemConfig.swift +++ b/punchnet/Core/SystemConfig.swift @@ -21,7 +21,7 @@ struct SystemConfig { case prod = "prod" } - static let env: Env = .dev + static let env: Env = .prod // 版本设置 static let version: Int = 1 @@ -65,7 +65,7 @@ struct SystemConfig { accessToken: String, identityId: UInt32, hostname: String, - acl: NetworkContext.ACL, + exposedService: NetworkContext.ExposedService?, exitNodeIp: String?) -> [String: NSObject] { let envConfig = getCurrentEnvConfig() @@ -81,9 +81,9 @@ struct SystemConfig { "stun_assist_host": envConfig.stunAssistHost as NSObject, "hostname": hostname as NSObject, "env": Self.env.rawValue as NSObject, - "acl": [ - "tcp": acl.tcp as NSObject, - "udp": acl.udp as NSObject + "exposed_service": [ + "tcp": (exposedService?.tcp ?? []) as NSObject, + "udp": (exposedService?.udp ?? []) as NSObject ] as NSObject, "network_address": [ "network_id": networkId as NSObject, diff --git a/punchnet/Shared/Models/NetworkContext.swift b/punchnet/Shared/Models/NetworkContext.swift index 01660e9..c68ef17 100644 --- a/punchnet/Shared/Models/NetworkContext.swift +++ b/punchnet/Shared/Models/NetworkContext.swift @@ -15,7 +15,7 @@ struct NetworkContext: Codable { let identityId: UInt32 let resourceList: [Resource] let nodeList: [Node] - let acl: ACL + let exposedService: ExposedService? let exitNodeList: [ExitNode] enum CodingKeys: String, CodingKey { @@ -25,7 +25,7 @@ struct NetworkContext: Codable { case identityId = "identity_id" case resourceList = "resource_list" case nodeList = "node_list" - case acl = "acl" + case exposedService = "exposed_service" case exitNodeList = "exit_node" } @@ -40,7 +40,7 @@ struct NetworkContext: Codable { extension NetworkContext { - struct ACL: Codable { + struct ExposedService: Codable { let tcp: [UInt32] let udp: [UInt32] }