fix quicClient

This commit is contained in:
anlicheng 2026-04-28 16:23:01 +08:00
parent 26dc6b5152
commit d900fdb379
2 changed files with 128 additions and 82 deletions

View File

@ -181,36 +181,33 @@ actor SDLContextActor {
try await Task.sleep(for: .seconds(0.3)) try await Task.sleep(for: .seconds(0.3))
SDLLogger.log("[SDLContext] start quic client: \(self.config.serverHost)") SDLLogger.log("[SDLContext] start quic client: \(self.config.serverHost)")
// self.quicWorker = Task {
// for await message in await quicClient.messageStream {
// await self.handleQUICMessage(message: message)
// }
// }
try await withThrowingTaskGroup { group in try await withThrowingTaskGroup { group in
defer { defer {
group.cancelAll() group.cancelAll()
} }
group.addTask { group.addTask {
for await message in await quicClient.messageStream { for await message in await quicClient.messageStream {
await self.handleQUICMessage(message: message) await self.handleQUICMessage(message: message)
} }
throw SDLQUICClientExit.transportClosed("messageStream finished")
} }
group.addTask { group.addTask {
let exit = await quicClient.run() let exit = await quicClient.run()
switch exit { switch exit {
case .normal, .cancelled: case .normal:
return return
case .cancelled:
if Task.isCancelled {
return
}
throw exit
case .transportClosed, .readFailed, .writeFailed: case .transportClosed, .readFailed, .writeFailed:
throw exit throw exit
} }
} }
group.addTask { group.addTask {
for await event in await quicClient.eventStream { for await event in await quicClient.eventStream {
switch event { switch event {
@ -222,11 +219,13 @@ actor SDLContextActor {
throw error throw error
} }
} }
throw SDLQUICClientExit.cancelled
} }
try await group.next()
while let _ = try await group.next() {
()
}
} }
} }
await self.supervisor.addWorker(name: "udpHole") { await self.supervisor.addWorker(name: "udpHole") {

View File

@ -66,6 +66,8 @@ actor SDLQUICClient {
public var eventStream: AsyncStream<SDLQUICEvent> public var eventStream: AsyncStream<SDLQUICEvent>
private let eventCont: AsyncStream<SDLQUICEvent>.Continuation private let eventCont: AsyncStream<SDLQUICEvent>.Continuation
private var didFinishStreams = false
private let connection: NWConnection private let connection: NWConnection
private let queue = DispatchQueue(label: "com.sdl.QUICClient.queue") // 线 private let queue = DispatchQueue(label: "com.sdl.QUICClient.queue") // 线
@ -92,64 +94,58 @@ actor SDLQUICClient {
} }
func start() { func start() {
connection.stateUpdateHandler = { state in connection.stateUpdateHandler = { [weak self] state in
SDLLogger.log("[SDLQUICClient] new state: \(state)", for: .debug) Task {
switch state { await self?.handleConnectionStateUpdate(state)
case .ready:
Task {
await self.readyState.markReady()
}
case .failed(let error):
Task {
await self.readyState.markFailed(error)
}
self.eventCont.yield(.failed(error))
case .cancelled:
Task {
await self.readyState.markCancelled()
}
self.eventCont.yield(.cancelled)
case .setup, .preparing:
Task {
await self.readyState.markConnecting()
}
default:
()
} }
} }
connection.start(queue: self.queue) connection.start(queue: self.queue)
} }
func waitReady(timeout: Duration = .seconds(5)) async throws { private func handleConnectionStateUpdate(_ state: NWConnection.State) async {
try await withThrowingTaskGroup(of: Void.self) { group in SDLLogger.log("[SDLQUICClient] new state: \(state)", for: .debug)
group.addTask { switch state {
try await self.readyState.waitReady() case .ready:
} await self.readyState.markReady()
case .failed(let error):
group.addTask { await self.readyState.markFailed(error)
try await Task.sleep(for: timeout) self.emitEvent(.failed(error))
throw SDLQUICError.timeout case .cancelled:
} await self.readyState.markCancelled()
self.emitEvent(.cancelled)
try await group.next() case .setup, .preparing:
group.cancelAll() await self.readyState.markConnecting()
default:
()
} }
} }
func waitReady(timeout: Duration = .seconds(5)) async throws {
try await self.readyState.waitReady(timeout: timeout)
}
func run() async -> SDLQUICClientExit { func run() async -> SDLQUICClientExit {
await withTaskGroup(of: SDLQUICClientExit.self) { group in await withTaskCancellationHandler {
group.addTask { await withTaskGroup(of: SDLQUICClientExit.self) { group in
await self.readLoop() group.addTask {
await self.readLoop()
}
group.addTask {
await self.heartbeatLoop()
}
let exit = await group.next() ?? .normal
group.cancelAll()
await self.stop()
self.finishStreams()
return exit
} }
} onCancel: {
group.addTask { Task {
await self.heartbeatLoop() await self.stop()
} }
let exit = await group.next() ?? .normal
group.cancelAll()
return exit
} }
} }
@ -163,7 +159,9 @@ actor SDLQUICClient {
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 {
SDLLogger.log("[SDLQUICClient] send data get error: \(error)", for: .debug) SDLLogger.log("[SDLQUICClient] send data get error: \(error)", for: .debug)
self?.eventCont.yield(.writeFailed(error)) Task {
await self?.emitEvent(.writeFailed(error))
}
} }
}) })
} }
@ -183,16 +181,33 @@ actor SDLQUICClient {
return .cancelled return .cancelled
} }
func stop() { func stop() async {
self.connection.cancel() self.connection.cancel()
await self.readyState.markCancelled()
self.finishStreams()
} }
func close(_ exit: SDLQUICClientExit = .normal) async { private func emitEvent(_ event: SDLQUICEvent) {
guard !self.didFinishStreams else {
return
}
self.eventCont.yield(event)
} }
deinit { private func finishStreams() {
guard !self.didFinishStreams else {
return
}
self.didFinishStreams = true
self.messageCont.finish() self.messageCont.finish()
self.eventCont.finish()
}
deinit {
self.connection.cancel()
self.finishStreams()
} }
} }
@ -209,26 +224,58 @@ extension SDLQUICClient {
} }
private var state: State = .idle private var state: State = .idle
private var continuations: [CheckedContinuation<Void, Error>] = [] private var continuations: [UUID: CheckedContinuation<Void, Error>] = [:]
func waitReady() async throws { func waitReady(timeout: Duration) async throws {
switch state { let id = UUID()
case .ready: let timeoutTask = Task {
return try? await Task.sleep(for: timeout)
if Task.isCancelled {
return
}
await self.cancelWaiter(id: id, throwing: SDLQUICError.timeout)
}
case .failed(let error): defer {
throw error timeoutTask.cancel()
}
case .cancelled: try await withTaskCancellationHandler {
throw CancellationError()
case .idle, .connecting:
try await withCheckedThrowingContinuation { continuation in try await withCheckedThrowingContinuation { continuation in
continuations.append(continuation) self.addWaiter(id: id, continuation: continuation)
}
} onCancel: {
timeoutTask.cancel()
Task {
await self.cancelWaiter(id: id, throwing: CancellationError())
} }
} }
} }
private func addWaiter(id: UUID, continuation: CheckedContinuation<Void, Error>) {
switch state {
case .ready:
continuation.resume()
case .failed(let error):
continuation.resume(throwing: error)
case .cancelled:
continuation.resume(throwing: CancellationError())
case .idle, .connecting:
continuations[id] = continuation
}
}
private func cancelWaiter(id: UUID, throwing error: Error) {
guard let continuation = continuations.removeValue(forKey: id) else {
return
}
continuation.resume(throwing: error)
}
func markConnecting() { func markConnecting() {
switch state { switch state {
case .idle: case .idle:
@ -244,7 +291,7 @@ extension SDLQUICClient {
let list = continuations let list = continuations
continuations.removeAll() continuations.removeAll()
for continuation in list { for continuation in list.values {
continuation.resume() continuation.resume()
} }
} }
@ -255,7 +302,7 @@ extension SDLQUICClient {
let list = continuations let list = continuations
continuations.removeAll() continuations.removeAll()
for continuation in list { for continuation in list.values {
continuation.resume(throwing: error) continuation.resume(throwing: error)
} }
} }
@ -266,7 +313,7 @@ extension SDLQUICClient {
let list = continuations let list = continuations
continuations.removeAll() continuations.removeAll()
for continuation in list { for continuation in list.values {
continuation.resume(throwing: CancellationError()) continuation.resume(throwing: CancellationError())
} }
} }