fix superClient

This commit is contained in:
anlicheng 2026-05-27 18:22:22 +08:00
parent 3cdf1953bf
commit 1c0ed04fa2
6 changed files with 93 additions and 48 deletions

View File

@ -146,7 +146,7 @@ extension SDLConfiguration {
guard let networkAddress = parseNetworkAddress(networkAddressDict) else { guard let networkAddress = parseNetworkAddress(networkAddressDict) else {
return nil return nil
} }
let acl = parseACL(options["acl"]) let acl = parseACL(options["exposed_service"])
// //
var exitNode: ExitNode? = nil var exitNode: ExitNode? = nil

View File

@ -8,20 +8,15 @@
import Foundation import Foundation
import Network import Network
actor SDLSuperClient { final class SDLSuperClient: @unchecked Sendable {
enum State {
case idle
case running
case stopped
}
private var state: State = .idle
private let queue = DispatchQueue(label: "com.sdl.SuperClient.queue") // 线 private let queue = DispatchQueue(label: "com.sdl.SuperClient.queue") // 线
// //
public let messageStream: AsyncThrowingStream<SDLQUICInboundMessage, Error> public let messageStream: AsyncThrowingStream<SDLQUICInboundMessage, Error>
private let messageCont: AsyncThrowingStream<SDLQUICInboundMessage, Error>.Continuation private let messageCont: AsyncThrowingStream<SDLQUICInboundMessage, Error>.Continuation
private let stateLock = NSLock()
private var isStarted = false
private var isStopped = false
private var isMessageContinuationFinished = false
private let connection: NWConnection private let connection: NWConnection
private let maxBufferSize: Int private let maxBufferSize: Int
@ -62,7 +57,7 @@ actor SDLSuperClient {
} }
func run() async throws { func run() async throws {
guard self.state == .idle else { guard self.markStarted() else {
return return
} }
@ -114,25 +109,32 @@ actor SDLSuperClient {
} }
private func runStateLoop(_ stateStream: AsyncThrowingStream<NWConnection.State, Error>) async throws { private func runStateLoop(_ stateStream: AsyncThrowingStream<NWConnection.State, Error>) 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<NWConnection.State, Error>) async throws {
for try await state in stateStream { for try await state in stateStream {
try Task.checkCancellation() try Task.checkCancellation()
switch state { if case .ready = state {
case .ready:
self.state = .running
try await self.readLoop()
return 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 { private func readLoop() async throws {
@ -154,18 +156,18 @@ actor SDLSuperClient {
} catch is CancellationError { } catch is CancellationError {
throw CancellationError() throw CancellationError()
} catch let error as SDLSuperError { } catch let error as SDLSuperError {
self.messageCont.finish(throwing: error) self.finishMessageStream(throwing: error)
throw error throw error
} catch { } catch {
let wrappedError = SDLSuperError.internalError(error) let wrappedError = SDLSuperError.internalError(error)
self.messageCont.finish(throwing: wrappedError) self.finishMessageStream(throwing: wrappedError)
throw wrappedError throw wrappedError
} }
} }
func send(type: SDLPacketType, data: Data) { func send(type: SDLPacketType, data: Data) {
guard case .running = state, connection.state == .ready else { guard connection.state == .ready else {
return return
} }
@ -176,10 +178,8 @@ actor SDLSuperClient {
connection.send(content: packet, completion: .contentProcessed { [weak self] error in connection.send(content: packet, completion: .contentProcessed { [weak self] error in
if let error { if let error {
Task { SDLLogger.log("[SDLSuperClient] send data get error: \(error)", for: .debug)
SDLLogger.log("[SDLSuperClient] send data get error: \(error)", for: .debug) self?.finishMessageStream(throwing: SDLSuperError.writeFailed(error))
self?.messageCont.finish(throwing: SDLSuperError.writeFailed(error))
}
} }
}) })
} }
@ -213,20 +213,65 @@ actor SDLSuperClient {
} }
func stop() { func stop() {
guard self.state != .stopped else { guard self.markStopped() else {
return return
} }
self.state = .stopped
let connection = self.connection let connection = self.connection
connection.stateUpdateHandler = nil connection.stateUpdateHandler = nil
connection.cancel() connection.cancel()
self.messageCont.finish() self.finishMessageStream()
SDLLogger.log("[SDLSuperClient] stopped") 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 { deinit {
SDLLogger.log("[SDLSuperClient] deinit") SDLLogger.log("[SDLSuperClient] deinit")
} }

View File

@ -32,11 +32,11 @@ final class SDLSuperSession: @unchecked Sendable {
} }
func stop() async { func stop() async {
await self.client.stop() self.client.stop()
} }
func send(type: SDLPacketType, data: Data) async { 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 { private func runLoops() async throws {
@ -74,7 +74,7 @@ final class SDLSuperSession: @unchecked Sendable {
while true { while true {
try await Task.sleep(for: .seconds(5)) try await Task.sleep(for: .seconds(5))
try Task.checkCancellation() try Task.checkCancellation()
await self.client.send(type: .ping, data: Data()) self.client.send(type: .ping, data: Data())
} }
} }
} }

View File

@ -144,7 +144,7 @@ class AppContext {
accessToken: session.accessToken, accessToken: session.accessToken,
identityId: context.identityId, identityId: context.identityId,
hostname: context.hostname, hostname: context.hostname,
acl: context.acl, exposedService: context.exposedService,
exitNodeIp: self.selectedExitNodeIp exitNodeIp: self.selectedExitNodeIp
) )
try await self.vpnManager.enableVpn(options: options) try await self.vpnManager.enableVpn(options: options)

View File

@ -21,7 +21,7 @@ struct SystemConfig {
case prod = "prod" case prod = "prod"
} }
static let env: Env = .dev static let env: Env = .prod
// //
static let version: Int = 1 static let version: Int = 1
@ -65,7 +65,7 @@ struct SystemConfig {
accessToken: String, accessToken: String,
identityId: UInt32, identityId: UInt32,
hostname: String, hostname: String,
acl: NetworkContext.ACL, exposedService: NetworkContext.ExposedService?,
exitNodeIp: String?) -> [String: NSObject] { exitNodeIp: String?) -> [String: NSObject] {
let envConfig = getCurrentEnvConfig() let envConfig = getCurrentEnvConfig()
@ -81,9 +81,9 @@ struct SystemConfig {
"stun_assist_host": envConfig.stunAssistHost as NSObject, "stun_assist_host": envConfig.stunAssistHost as NSObject,
"hostname": hostname as NSObject, "hostname": hostname as NSObject,
"env": Self.env.rawValue as NSObject, "env": Self.env.rawValue as NSObject,
"acl": [ "exposed_service": [
"tcp": acl.tcp as NSObject, "tcp": (exposedService?.tcp ?? []) as NSObject,
"udp": acl.udp as NSObject "udp": (exposedService?.udp ?? []) as NSObject
] as NSObject, ] as NSObject,
"network_address": [ "network_address": [
"network_id": networkId as NSObject, "network_id": networkId as NSObject,

View File

@ -15,7 +15,7 @@ struct NetworkContext: Codable {
let identityId: UInt32 let identityId: UInt32
let resourceList: [Resource] let resourceList: [Resource]
let nodeList: [Node] let nodeList: [Node]
let acl: ACL let exposedService: ExposedService?
let exitNodeList: [ExitNode] let exitNodeList: [ExitNode]
enum CodingKeys: String, CodingKey { enum CodingKeys: String, CodingKey {
@ -25,7 +25,7 @@ struct NetworkContext: Codable {
case identityId = "identity_id" case identityId = "identity_id"
case resourceList = "resource_list" case resourceList = "resource_list"
case nodeList = "node_list" case nodeList = "node_list"
case acl = "acl" case exposedService = "exposed_service"
case exitNodeList = "exit_node" case exitNodeList = "exit_node"
} }
@ -40,7 +40,7 @@ struct NetworkContext: Codable {
extension NetworkContext { extension NetworkContext {
struct ACL: Codable { struct ExposedService: Codable {
let tcp: [UInt32] let tcp: [UInt32]
let udp: [UInt32] let udp: [UInt32]
} }