// // SDLSuperClient.swift // Tun // // Created by 安礼成 on 2026/2/13. // import Foundation import Network // 定义错误类型,便于上层处理 enum SDLSuperError: Error { case connectionFailed(Error) case connectionCancelled case writeFailed(Error) case internalError(Error) case timeout case decodeError(String) case packetTooLarge case dataStreamClosed } actor SDLSuperClient { enum State { case idle case running case stopped } private var state: State = .idle private let frameParser: SDLSuperFrameParser // 数据流 public var messageStream: AsyncThrowingStream private let messageCont: AsyncThrowingStream.Continuation private var isMessageContinuationFinished: Bool = false private var readTask: Task? private var connection: NWConnection? private let serverEndpoint: SDLConfiguration.ResolvedServerEndpoint private let port: UInt16 init(serverEndpoint: SDLConfiguration.ResolvedServerEndpoint, port: UInt16, maxBufferSize: Int = 2 * 1024 * 1024) { self.serverEndpoint = serverEndpoint self.port = port self.frameParser = SDLSuperFrameParser(maxBufferSize: maxBufferSize) (self.messageStream, self.messageCont) = AsyncThrowingStream.makeStream(of: SDLQUICInboundMessage.self) } func start() { let serverEndpoint = self.serverEndpoint let queue = DispatchQueue(label: "com.sdl.SuperClient.queue") // 专用队列保证线程安全 let options = NWProtocolTLS.Options() serverEndpoint.host.withCString { sec_protocol_options_set_tls_server_name(options.securityProtocolOptions, $0) } sec_protocol_options_add_tls_application_protocol( options.securityProtocolOptions, "punchnet/1.0" ) // 这里设置证书的校验逻辑 sec_protocol_options_set_verify_block( options.securityProtocolOptions, { _, trust, complete in // 执行公钥校验 complete(SDLSuperTLSVerifier.verify(trust: trust, host: serverEndpoint.host)) }, queue ) SDLLogger.log("[SDLSuperClient] start with tls protocol", for: .debug) let params = NWParameters(tls: options) // 关键:让 Network.framework 忽略系统代理 params.preferNoProxies = true let connection = NWConnection(host: Self.makeEndpointHost(address: serverEndpoint.ip), port: .init(rawValue: port)!, using: params) connection.stateUpdateHandler = { [weak self] state in SDLLogger.log("[SDLSuperClient] new state: \(state)", for: .debug) Task { await self?.handleConnectionState(state: state) } } connection.start(queue: queue) self.connection = connection } private static func makeEndpointHost(address ip: String) -> NWEndpoint.Host { if let ipv4Address = IPv4Address(ip) { return .ipv4(ipv4Address) } if let ipv6Address = IPv6Address(ip) { return .ipv6(ipv6Address) } preconditionFailure("invalid super server IP: \(ip)") } private func handleConnectionState(state: NWConnection.State) { switch state { case .ready: self.startReadTask() self.state = .running case .failed(let error): self.finishMessageContinuationIfNeed(throwing: .connectionFailed(error)) case .cancelled: self.finishMessageContinuationIfNeed(throwing: .connectionCancelled) default: () } } private func finishMessageContinuationIfNeed(throwing error: SDLSuperError?) { guard !self.isMessageContinuationFinished else { return } self.isMessageContinuationFinished = true if let error { self.messageCont.finish(throwing: error) } else { self.messageCont.finish() } } private func startReadTask() { self.readTask?.cancel() self.readTask = Task { do { while true { try Task.checkCancellation() let data = try await self.readOnce() let frames = try self.frameParser.parseFrames(data: data) for frame in frames { try Task.checkCancellation() if let message = SDLSuperCodec.decode(frame: frame) { self.messageCont.yield(message) } else { self.finishMessageContinuationIfNeed(throwing: .decodeError("invalid message")) } } } } catch let err { self.finishMessageContinuationIfNeed(throwing: .internalError(err)) } } } func send(type: SDLPacketType, data: Data) { guard case .running = state, let connection = self.connection, connection.state == .ready else { return } var len = UInt16(data.count + 1).bigEndian var packet = Data(Data(bytes: &len, count: 2)) packet.append(type.rawValue) packet.append(data) connection.send(content: packet, completion: .contentProcessed { [weak self] error in if let error { Task { SDLLogger.log("[SDLSuperClient] send data get error: \(error)", for: .debug) await self?.finishMessageContinuationIfNeed(throwing: .writeFailed(error)) } } }) } private func readOnce() async throws -> Data { guard let connection = self.connection else { throw SDLSuperError.connectionCancelled } let readContinuation = OnceContinuation() return try await withTaskCancellationHandler { try await withCheckedThrowingContinuation { cont in readContinuation.set(cont) connection.receive(minimumIncompleteLength: 1, maximumLength: 64 * 1024) { data, _, isComplete, error in if let error { readContinuation.resume(throwing: error) return } if isComplete { readContinuation.resume(throwing: SDLSuperError.dataStreamClosed) } else { readContinuation.resume(returning: data ?? Data()) } } } } onCancel: { readContinuation.resume(throwing: CancellationError()) } } func stop() { guard self.state != .stopped else { return } self.state = .stopped self.readTask?.cancel() self.readTask = nil let connection = self.connection self.connection = nil connection?.stateUpdateHandler = nil connection?.cancel() self.finishMessageContinuationIfNeed(throwing: nil) SDLLogger.log("[SDLSuperClient] stopped") } deinit { SDLLogger.log("[SDLSuperClient] deinit") } }