fix quicClient

This commit is contained in:
anlicheng 2026-04-28 16:12:59 +08:00
parent a70419e3e2
commit 3c42aa58f3
5 changed files with 265 additions and 173 deletions

View File

@ -70,7 +70,7 @@ actor ArpServer {
self.coolingDown = [:] self.coolingDown = [:]
} }
func arpRequest(targetIp: UInt32, use quicClient: SDLQUICClient?) throws { func arpRequest(targetIp: UInt32, use quicClient: SDLQUICClient?) async throws {
guard let quicClient, self.coolingDown[targetIp] == nil else { guard let quicClient, self.coolingDown[targetIp] == nil else {
return return
} }
@ -82,7 +82,7 @@ actor ArpServer {
var arpRequest = SDLArpRequest() var arpRequest = SDLArpRequest()
arpRequest.targetIp = targetIp arpRequest.targetIp = targetIp
quicClient.send(type: .arpRequest, data: try arpRequest.serializedData()) await quicClient.send(type: .arpRequest, data: try arpRequest.serializedData())
} }
func handleArpResponse(arpResponse: SDLArpResponse) { func handleArpResponse(arpResponse: SDLArpResponse) {

View File

@ -166,18 +166,69 @@ actor SDLContextActor {
await self.supervisor.addWorker(name: "quicClient") { await self.supervisor.addWorker(name: "quicClient") {
SDLLogger.log("[SDLContext] try start quicClient", for: .debug) SDLLogger.log("[SDLContext] try start quicClient", for: .debug)
let quicClient = try await self.startQUICClient()
SDLLogger.log("[SDLContext] quicClient running!!!!") self.quicWorker?.cancel()
await self.quicClient?.stop()
// monitor
let quicClient = SDLQUICClient(host: self.config.serverHost, port: 443)
self.quicClient = quicClient
await quicClient.start()
try await quicClient.waitReady(timeout: .seconds(3))
// quic
try await Task.sleep(for: .seconds(0.3))
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
defer {
group.cancelAll()
}
group.addTask {
for await message in await quicClient.messageStream {
await self.handleQUICMessage(message: message)
}
throw SDLQUICClientExit.transportClosed("messageStream finished")
}
group.addTask {
let exit = await quicClient.run() let exit = await quicClient.run()
SDLLogger.log("[SDLContext] quicClient closed: \(exit)")
switch exit { switch exit {
case .normal, .cancelled: case .normal, .cancelled:
return return
case .transportClosed, .readFailed, .writeFailed: case .transportClosed, .readFailed, .writeFailed:
throw exit throw exit
} }
} }
group.addTask {
for await event in await quicClient.eventStream {
switch event {
case .failed(let error):
throw error
case .cancelled:
throw SDLQUICClientExit.cancelled
case .writeFailed(let error):
throw error
}
}
throw SDLQUICClientExit.cancelled
}
try await group.next()
}
}
await self.supervisor.addWorker(name: "udpHole") { await self.supervisor.addWorker(name: "udpHole") {
let udpHole = try await self.startUDPHole() let udpHole = try await self.startUDPHole()
SDLLogger.log("[SDLContext] udp running!!!!") SDLLogger.log("[SDLContext] udp running!!!!")
@ -247,7 +298,7 @@ actor SDLContextActor {
return return
} }
await self.handleReadyTimeout() self.handleReadyTimeout()
} }
defer { defer {
timeoutTask.cancel() timeoutTask.cancel()
@ -266,27 +317,12 @@ actor SDLContextActor {
try await self.setNetworkSettings(config: config, dnsServer: DNSHelper.dnsServer) try await self.setNetworkSettings(config: config, dnsServer: DNSHelper.dnsServer)
} }
private func startQUICClient() async throws -> SDLQUICClient { private func handleQUICMessage(message: SDLQUICInboundMessage) async {
self.quicWorker?.cancel()
self.quicClient?.stop()
// monitor
let quicClient = SDLQUICClient(host: self.config.serverHost, port: 443)
quicClient.start()
// quic
try await quicClient.waitReady()
// quic
try await Task.sleep(for: .seconds(0.2))
SDLLogger.log("[SDLContext] start quic client: \(self.config.serverHost)")
self.quicWorker = Task.detached {
for await message in quicClient.messageStream {
switch message { switch message {
case .welcome(let welcome): case .welcome(let welcome):
SDLLogger.log("[SDLContext] quic welcome: \(welcome)") SDLLogger.log("[SDLContext] quic welcome: \(welcome)")
// //
await self.startRegisterLoop() self.startRegisterLoop()
// stun // stun
await self.startStunRequestTask(welcome: welcome) await self.startStunRequestTask(welcome: welcome)
@ -296,7 +332,7 @@ actor SDLContextActor {
case .registerSuperAck(let registerSuperAck): case .registerSuperAck(let registerSuperAck):
await self.handleRegisterSuperAck(registerSuperAck: registerSuperAck) await self.handleRegisterSuperAck(registerSuperAck: registerSuperAck)
case .registerSuperNak(let registerSuperNak): case .registerSuperNak(let registerSuperNak):
await self.handleRegisterSuperNak(nakPacket: registerSuperNak) self.handleRegisterSuperNak(nakPacket: registerSuperNak)
case .peerInfo(let peerInfo): case .peerInfo(let peerInfo):
//SDLLogger.shared.log("[SDLContext] peer message: \(peerInfo)") //SDLLogger.shared.log("[SDLContext] peer message: \(peerInfo)")
await self.puncherActor.handlePeerInfo(using: self.udpHole, udpHoleV6: self.udpHoleV6, peerInfo: peerInfo) await self.puncherActor.handlePeerInfo(using: self.udpHole, udpHoleV6: self.udpHoleV6, peerInfo: peerInfo)
@ -310,11 +346,6 @@ actor SDLContextActor {
await self.arpServer.handleArpResponse(arpResponse: arpResponse) await self.arpServer.handleArpResponse(arpResponse: arpResponse)
} }
} }
}
self.quicClient = quicClient
return quicClient
}
private func prepareTunnelNotifier() { private func prepareTunnelNotifier() {
// noticeClient // noticeClient
@ -475,7 +506,7 @@ actor SDLContextActor {
self.quicWorker?.cancel() self.quicWorker?.cancel()
self.quicWorker = nil self.quicWorker = nil
self.quicClient?.stop() await self.quicClient?.stop()
self.quicClient = nil self.quicClient = nil
await self.dnsClient?.stop() await self.dnsClient?.stop()
@ -810,7 +841,7 @@ extension SDLContextActor {
while !Task.isCancelled { while !Task.isCancelled {
switch self.superRegistrationStateMachine.makeLoopAction() { switch self.superRegistrationStateMachine.makeLoopAction() {
case .sendRegister: case .sendRegister:
self.doRegisterSuper() await self.doRegisterSuper()
case .stop: case .stop:
return return
} }
@ -922,7 +953,7 @@ extension SDLContextActor {
} }
} }
private func doRegisterSuper() { private func doRegisterSuper() async {
// //
var registerSuper = SDLRegisterSuper() var registerSuper = SDLRegisterSuper()
registerSuper.clientID = self.config.clientId registerSuper.clientID = self.config.clientId
@ -936,7 +967,7 @@ extension SDLContextActor {
if let registerSuperData = try? registerSuper.serializedData() { if let registerSuperData = try? registerSuper.serializedData() {
SDLLogger.log("[SDLContext] will send register super") SDLLogger.log("[SDLContext] will send register super")
self.quicClient?.send(type: .registerSuper, data: registerSuperData) await self.quicClient?.send(type: .registerSuper, data: registerSuperData)
} }
} }

View File

@ -64,7 +64,7 @@ actor SDLPuncherActor {
} }
} }
func submitRegisterRequest(quicClient: SDLQUICClient?, request: RegisterRequest) { func submitRegisterRequest(quicClient: SDLQUICClient?, request: RegisterRequest) async {
guard let quicClient else { guard let quicClient else {
return return
} }
@ -90,7 +90,7 @@ actor SDLPuncherActor {
phase: .waitingPeerInfo(deadline: now.addingTimeInterval(self.peerInfoTimeout)) phase: .waitingPeerInfo(deadline: now.addingTimeInterval(self.peerInfoTimeout))
) )
quicClient.send(type: .queryInfo, data: queryData) await quicClient.send(type: .queryInfo, data: queryData)
} }
func handlePeerInfo(using udpHole: SDLUDPHole?, udpHoleV6: SDLUDPHoleV6?, peerInfo: SDLPeerInfo) async { func handlePeerInfo(using udpHole: SDLUDPHole?, udpHoleV6: SDLUDPHoleV6?, peerInfo: SDLPeerInfo) async {

View File

@ -20,6 +20,12 @@ enum SDLQUICError: Error {
case packetTooLarge case packetTooLarge
} }
enum SDLQUICEvent: Error {
case failed(Error)
case cancelled
case writeFailed(Error)
}
enum SDLQUICClientExit: Error, Sendable, CustomStringConvertible { enum SDLQUICClientExit: Error, Sendable, CustomStringConvertible {
case normal case normal
case cancelled case cancelled
@ -43,59 +49,36 @@ enum SDLQUICClientExit: Error, Sendable, CustomStringConvertible {
} }
} }
private actor SDLQUICCloseWait { actor SDLQUICClient {
private var exit: SDLQUICClientExit?
private var waiters: [CheckedContinuation<SDLQUICClientExit, Never>] = []
func wait() async -> SDLQUICClientExit {
if let exit {
return exit
}
return await withCheckedContinuation { continuation in
waiters.append(continuation)
}
}
func close(_ exit: SDLQUICClientExit) {
guard self.exit == nil else {
return
}
self.exit = exit
let waiters = self.waiters
self.waiters.removeAll()
for waiter in waiters {
waiter.resume(returning: exit)
}
}
}
final class SDLQUICClient {
private let allocator = ByteBufferAllocator() private let allocator = ByteBufferAllocator()
// 64K // 64K
private let maxPacketSize: Int private let maxPacketSize: Int
// 2M // 2M
private let maxBufferSize: Int private let maxBufferSize: Int
private let readyState = SDLQUICReadyState()
//
public var messageStream: AsyncStream<SDLQUICInboundMessage> public var messageStream: AsyncStream<SDLQUICInboundMessage>
private let messageCont: AsyncStream<SDLQUICInboundMessage>.Continuation private let messageCont: AsyncStream<SDLQUICInboundMessage>.Continuation
//
public var eventStream: AsyncStream<SDLQUICEvent>
private let eventCont: AsyncStream<SDLQUICEvent>.Continuation
private var readTask: Task<Void, Never>? private var readTask: Task<Void, Never>?
private var pingTask: Task<Void, Never>? private var pingTask: Task<Void, Never>?
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") // 线
private let closeWait = SDLQUICCloseWait()
private let (readyStream, readyCont) = AsyncStream.makeStream(of: Void.self)
init(host: String, port: UInt16, maxPacketSize: Int = 64 * 1024, maxBufferSize: Int = 2 * 1024 * 1024) { init(host: String, port: UInt16, maxPacketSize: Int = 64 * 1024, maxBufferSize: Int = 2 * 1024 * 1024) {
let options = NWProtocolQUIC.Options(alpn: ["punchnet/1.0"]) let options = NWProtocolQUIC.Options(alpn: ["punchnet/1.0"])
self.maxBufferSize = maxBufferSize self.maxBufferSize = maxBufferSize
self.maxPacketSize = maxPacketSize self.maxPacketSize = maxPacketSize
(self.messageStream, self.messageCont) = AsyncStream.makeStream(of: SDLQUICInboundMessage.self) (self.messageStream, self.messageCont) = AsyncStream.makeStream(of: SDLQUICInboundMessage.self)
(self.eventStream, self.eventCont) = AsyncStream.makeStream(of: SDLQUICEvent.self)
// TODO // TODO
sec_protocol_options_set_verify_block( sec_protocol_options_set_verify_block(
@ -116,17 +99,22 @@ final class SDLQUICClient {
SDLLogger.log("[SDLQUICClient] new state: \(state)", for: .debug) SDLLogger.log("[SDLQUICClient] new state: \(state)", for: .debug)
switch state { switch state {
case .ready: case .ready:
self.readyCont.yield()
self.readyCont.finish()
case .failed(let error):
self.readyCont.finish()
Task { Task {
await self.close(.transportClosed("failed: \(error)")) await self.readyState.markReady()
} }
case .cancelled: case .failed(let error):
self.readyCont.finish()
Task { Task {
await self.close(.cancelled) 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: default:
() ()
@ -135,33 +123,37 @@ final class SDLQUICClient {
connection.start(queue: self.queue) connection.start(queue: self.queue)
} }
func waitReady(timeout: Duration = .seconds(5)) async throws {
try await withThrowingTaskGroup(of: Void.self) { group in
group.addTask {
try await self.readyState.waitReady()
}
group.addTask {
try await Task.sleep(for: timeout)
throw SDLQUICError.timeout
}
try await group.next()
group.cancelAll()
}
}
func run() async -> SDLQUICClientExit { func run() async -> SDLQUICClientExit {
await withTaskCancellationHandler {
await withTaskGroup(of: SDLQUICClientExit.self) { group in await withTaskGroup(of: SDLQUICClientExit.self) { group in
group.addTask { group.addTask {
await self.readLoop() await self.readLoop()
} }
group.addTask { group.addTask {
await self.heartbeatLoop() await self.heartbeatLoop()
} }
group.addTask {
await self.waitClose()
}
let exit = await group.next() ?? .normal let exit = await group.next() ?? .normal
group.cancelAll() group.cancelAll()
self.connection.cancel()
self.messageCont.finish()
await self.close(exit)
return exit return exit
} }
} onCancel: {
Task {
await self.close(.cancelled)
self.connection.cancel()
self.messageCont.finish()
}
}
} }
func send(type: SDLPacketType, data: Data) { func send(type: SDLPacketType, data: Data) {
@ -171,26 +163,27 @@ final class SDLQUICClient {
packet.append(type.rawValue) packet.append(type.rawValue)
packet.append(data) packet.append(data)
connection.send(content: packet, completion: .contentProcessed { 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)
Task { self?.eventCont.yield(.writeFailed(error))
await self.close(.writeFailed("\(error)"))
}
} }
}) })
} }
func waitReady() async throws { private func heartbeatLoop() async -> SDLQUICClientExit {
for await _ in readyStream { let timerStream = SDLAsyncTimerStream()
return timerStream.start(interval: .seconds(5))
for await _ in timerStream.stream {
if Task.isCancelled {
break
} }
let exit = await closeWait.wait() self.send(type: .ping, data: Data())
throw exit
} }
func waitClose() async -> SDLQUICClientExit { SDLLogger.log("[SDLQUICClient] udp pingTask cancel", for: .debug)
await closeWait.wait() return .cancelled
} }
func stop() { func stop() {
@ -198,9 +191,95 @@ final class SDLQUICClient {
} }
func close(_ exit: SDLQUICClientExit = .normal) async { func close(_ exit: SDLQUICClientExit = .normal) async {
await closeWait.close(exit)
} }
deinit {
self.readTask?.cancel()
self.pingTask?.cancel()
self.messageCont.finish()
}
}
// --MARK: Ready
extension SDLQUICClient {
actor SDLQUICReadyState {
enum State {
case idle
case connecting
case ready
case failed(Error)
case cancelled
}
private var state: State = .idle
private var continuations: [CheckedContinuation<Void, Error>] = []
func waitReady() async throws {
switch state {
case .ready:
return
case .failed(let error):
throw error
case .cancelled:
throw CancellationError()
case .idle, .connecting:
try await withCheckedThrowingContinuation { continuation in
continuations.append(continuation)
}
}
}
func markConnecting() {
switch state {
case .idle:
state = .connecting
default:
break
}
}
func markReady() {
state = .ready
let list = continuations
continuations.removeAll()
for continuation in list {
continuation.resume()
}
}
func markFailed(_ error: Error) {
state = .failed(error)
let list = continuations
continuations.removeAll()
for continuation in list {
continuation.resume(throwing: error)
}
}
func markCancelled() {
state = .cancelled
let list = continuations
continuations.removeAll()
for continuation in list {
continuation.resume(throwing: CancellationError())
}
}
}
}
// --MARK: Reader
extension SDLQUICClient {
private func readLoop() async -> SDLQUICClientExit { private func readLoop() async -> SDLQUICClientExit {
var buffer = allocator.buffer(capacity: self.maxBufferSize) var buffer = allocator.buffer(capacity: self.maxBufferSize)
let threshold = self.maxBufferSize / 10 * 6 let threshold = self.maxBufferSize / 10 * 6
@ -238,21 +317,6 @@ final class SDLQUICClient {
} }
} }
private func heartbeatLoop() async -> SDLQUICClientExit {
let timerStream = SDLAsyncTimerStream()
timerStream.start(interval: .seconds(5))
for await _ in timerStream.stream {
if Task.isCancelled {
break
}
self.send(type: .ping, data: Data())
}
SDLLogger.log("[SDLQUICClient] udp pingTask cancel", for: .debug)
return .cancelled
}
// //
private func parseFrames(buffer: inout ByteBuffer) throws -> [ByteBuffer] { private func parseFrames(buffer: inout ByteBuffer) throws -> [ByteBuffer] {
guard buffer.readableBytes >= 2 else { guard buffer.readableBytes >= 2 else {
@ -294,8 +358,10 @@ final class SDLQUICClient {
} }
} }
} }
}
// --MARK: // --MARK:
extension SDLQUICClient {
private func decode(frame: ByteBuffer) -> SDLQUICInboundMessage? { private func decode(frame: ByteBuffer) -> SDLQUICInboundMessage? {
var buffer = frame var buffer = frame
guard let type = buffer.readInteger(as: UInt8.self), guard let type = buffer.readInteger(as: UInt8.self),
@ -356,14 +422,9 @@ final class SDLQUICClient {
return nil return nil
} }
} }
deinit {
self.readTask?.cancel()
self.pingTask?.cancel()
self.messageCont.finish()
}
} }
// --MARK: quic
extension SDLQUICClient { extension SDLQUICClient {
enum QUICVerifier { enum QUICVerifier {

View File

@ -26,12 +26,12 @@ actor IdentityStore {
} }
// , quicClient // , quicClient
func batUpdatePolicy(using quicClient: SDLQUICClient?, dstIdentityID: UInt32) { func batUpdatePolicy(using quicClient: SDLQUICClient?, dstIdentityID: UInt32) async {
guard let quicClient else { guard let quicClient else {
return return
} }
self.identityMap.keys.forEach { identityId in for identityId in self.identityMap.keys {
var policyRequest = SDLPolicyRequest() var policyRequest = SDLPolicyRequest()
policyRequest.srcIdentityID = identityId policyRequest.srcIdentityID = identityId
policyRequest.dstIdentityID = dstIdentityID policyRequest.dstIdentityID = dstIdentityID
@ -39,13 +39,13 @@ actor IdentityStore {
// //
if let queryData = try? policyRequest.serializedData() { if let queryData = try? policyRequest.serializedData() {
quicClient.send(type: .policyRequest, data: queryData) await quicClient.send(type: .policyRequest, data: queryData)
} }
} }
} }
// //
func policyRequest(srcIdentityId: UInt32, dstIdentityId: UInt32, using quicClient: SDLQUICClient?) { func policyRequest(srcIdentityId: UInt32, dstIdentityId: UInt32, using quicClient: SDLQUICClient?) async {
guard let quicClient, !coolingDown.contains(srcIdentityId) else { guard let quicClient, !coolingDown.contains(srcIdentityId) else {
return return
} }
@ -59,7 +59,7 @@ actor IdentityStore {
coolingDown.insert(srcIdentityId) coolingDown.insert(srcIdentityId)
// //
if let queryData = try? policyRequest.serializedData() { if let queryData = try? policyRequest.serializedData() {
quicClient.send(type: .policyRequest, data: queryData) await quicClient.send(type: .policyRequest, data: queryData)
} }
Task { Task {