Compare commits

...

2 Commits

Author SHA1 Message Date
385edf3c6c 调整生命周期的管理 2026-05-05 16:40:16 +08:00
3bec145f23 调整生命周期的管理 2026-05-05 15:57:14 +08:00
4 changed files with 124 additions and 105 deletions

View File

@ -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,71 +166,48 @@ actor SDLContextActor {
// monitor // monitor
let quicClient = SDLQUICClient(host: self.config.serverHost, port: 1443) let quicClient = SDLQUICClient(host: self.config.serverHost, port: 1443)
self.quicClient = quicClient self.quicClient = quicClient
quicClient.start() await quicClient.start()
defer { defer {
self.quicClient?.stop() Task {
self.quicClient = nil await self.quicClient?.stop()
SDLLogger.log("[SDLContext] quicClient: stop") self.quicClient = nil
SDLLogger.log("[SDLContext] quicClient: stop")
}
} }
// quic // quic
try await Task.sleep(for: .seconds(0.5)) try await Task.sleep(for: .seconds(0.5))
SDLLogger.log("[SDLContext] start quic client: \(self.config.serverHost)") SDLLogger.log("[SDLContext] start quic client: \(self.config.serverHost)")
try await withThrowingTaskGroup { group in try await withTaskCancellationHandler {
defer { try await withThrowingTaskGroup { group in
group.cancelAll() defer {
} group.cancelAll()
}
// Actor AsyncStream
let (readyStream, readyContinuation) = AsyncStream<Void>.makeStream() group.addTask {
for try await message in await quicClient.messageStream {
group.addTask { try Task.checkCancellation()
for await event in quicClient.eventStream { await self.handleQUICMessage(message: message)
try Task.checkCancellation()
switch event {
case .ready:
readyContinuation.yield()
case .failed(let error):
throw error
case .cancelled:
throw SDLQUICEvent.cancelled
case .writeFailed(let error):
throw error
} }
} }
}
group.addTask {
//
var it = readyStream.makeAsyncIterator()
await it.next()
try Task.checkCancellation() group.addTask {
while true {
await withThrowingTaskGroup { workerGroup in try await Task.sleep(for: .seconds(5))
workerGroup.addTask { try Task.checkCancellation()
for await message in quicClient.messageStream { await quicClient.send(type: .ping, data: Data())
try Task.checkCancellation()
await self.handleQUICMessage(message: message)
}
}
workerGroup.addTask {
let timerStream = SDLAsyncTimerStream()
timerStream.start(interval: .seconds(5))
for await _ in timerStream.stream {
try Task.checkCancellation()
quicClient.send(type: .ping, data: Data())
}
SDLLogger.log("[SDLQUICClient] udp pingTask cancel", for: .debug)
} }
SDLLogger.log("[SDLQUICClient] udp pingTask cancel", for: .debug)
} }
try await group.next()
}
} onCancel: {
Task {
await quicClient.stop()
} }
try await group.next()
} }
} }
@ -880,7 +857,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

@ -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

@ -15,55 +15,61 @@ import Security
enum SDLQUICError: Error { enum SDLQUICError: Error {
case connectionFailed(Error) case connectionFailed(Error)
case connectionCancelled case connectionCancelled
case writeFailed(Error)
case internalError(Error)
case timeout case timeout
case decodeError(String) case decodeError(String)
case packetTooLarge case packetTooLarge
case dataStreamClosed case dataStreamClosed
} }
enum SDLQUICEvent: Error { actor SDLQUICClient {
case ready enum State {
case failed(Error) case idle
case cancelled case running
case writeFailed(Error) case stopped
} }
final class SDLQUICClient { private var state: State = .idle
private let frameParser: SDLQUICFrameParser private let frameParser: SDLQUICFrameParser
// //
public var messageStream: AsyncStream<SDLQUICInboundMessage> public var messageStream: AsyncThrowingStream<SDLQUICInboundMessage, Error>
private let messageCont: AsyncStream<SDLQUICInboundMessage>.Continuation private let messageCont: AsyncThrowingStream<SDLQUICInboundMessage, Error>.Continuation
private var isMessageContinuationFinished: Bool = false
private var readTask: Task<Void, Never>? private var readTask: Task<Void, Never>?
// private var connection: NWConnection?
public var eventStream: AsyncStream<SDLQUICEvent>
private let eventCont: AsyncStream<SDLQUICEvent>.Continuation
private var isFinished: Bool = false
private let connection: NWConnection
private let queue = DispatchQueue(label: "com.sdl.QUICClient.queue") // 线 private let queue = DispatchQueue(label: "com.sdl.QUICClient.queue") // 线
private let host: String
private let port: UInt16
init(host: String, port: UInt16, maxBufferSize: Int = 2 * 1024 * 1024) { init(host: String, port: UInt16, maxBufferSize: Int = 2 * 1024 * 1024) {
let options = NWProtocolTLS.Options() self.host = host
self.port = port
self.frameParser = SDLQUICFrameParser(maxBufferSize: maxBufferSize)
(self.messageStream, self.messageCont) = AsyncThrowingStream.makeStream(of: SDLQUICInboundMessage.self)
}
func start() {
let options = NWProtocolTLS.Options()
sec_protocol_options_add_tls_application_protocol( sec_protocol_options_add_tls_application_protocol(
options.securityProtocolOptions, options.securityProtocolOptions,
"punchnet/1.0" "punchnet/1.0"
) )
self.frameParser = SDLQUICFrameParser(maxBufferSize: maxBufferSize)
(self.eventStream, self.eventCont) = AsyncStream.makeStream(of: SDLQUICEvent.self)
(self.messageStream, self.messageCont) = AsyncStream.makeStream(of: SDLQUICInboundMessage.self)
// //
sec_protocol_options_set_verify_block( sec_protocol_options_set_verify_block(
options.securityProtocolOptions, options.securityProtocolOptions,
{ metadata, trust, complete in { metadata, trust, complete in
// //
complete(TLSVerifier.verify(trust: trust, host: host)) complete(TLSVerifier.verify(trust: trust, host: self.host))
}, },
self.queue self.queue
) )
@ -73,64 +79,95 @@ final class SDLQUICClient {
// Network.framework // Network.framework
params.preferNoProxies = true params.preferNoProxies = true
self.connection = NWConnection(host: .init(host), port: .init(rawValue: port)!, using: params) let connection = NWConnection(host: .init(host), port: .init(rawValue: port)!, using: params)
}
func start() {
connection.stateUpdateHandler = { [weak self] state in connection.stateUpdateHandler = { [weak self] state in
SDLLogger.log("[SDLQUICClient] new state: \(state)", for: .debug) SDLLogger.log("[SDLQUICClient] new state: \(state)", for: .debug)
switch state { Task {
case .ready: await self?.handleConnectionState(state: state)
self?.startReadTask()
self?.eventCont.yield(.ready)
case .failed(let error):
self?.eventCont.yield(.failed(error))
case .cancelled:
self?.eventCont.yield(.cancelled)
default:
()
} }
} }
connection.start(queue: self.queue) connection.start(queue: self.queue)
self.connection = connection
}
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: SDLQUICError?) {
guard !self.isMessageContinuationFinished else {
return
}
self.isMessageContinuationFinished = true
if let error {
self.messageCont.finish(throwing: error)
} else {
self.messageCont.finish()
}
} }
private func startReadTask() { private func startReadTask() {
self.readTask?.cancel() self.readTask?.cancel()
self.readTask = Task { self.readTask = Task {
do { do {
while !Task.isCancelled { while true {
try Task.checkCancellation()
let data = try await self.readOnce() let data = try await self.readOnce()
let frames = try self.frameParser.parseFrames(data: data) let frames = try self.frameParser.parseFrames(data: data)
for frame in frames { for frame in frames {
if let message = SDLQUICCodec.decode(frame: frame) { if let message = SDLQUICCodec.decode(frame: frame) {
self.messageCont.yield(message) self.messageCont.yield(message)
} else {
self.finishMessageContinuationIfNeed(throwing: .decodeError("invalid message"))
} }
} }
} }
} catch { } catch let err {
self.messageCont.finish() self.finishMessageContinuationIfNeed(throwing: .internalError(err))
} }
} }
} }
func send(type: SDLPacketType, data: Data) { 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 len = UInt16(data.count + 1).bigEndian
var packet = Data(Data(bytes: &len, count: 2)) var packet = Data(Data(bytes: &len, count: 2))
packet.append(type.rawValue) packet.append(type.rawValue)
packet.append(data) packet.append(data)
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) Task {
self?.eventCont.yield(.writeFailed(error)) SDLLogger.log("[SDLQUICClient] send data get error: \(error)", for: .debug)
await self?.finishMessageContinuationIfNeed(throwing: .writeFailed(error))
}
} }
}) })
} }
private func readOnce() async throws -> Data { private func readOnce() async throws -> Data {
guard let connection = self.connection else {
throw SDLQUICError.connectionCancelled
}
return try await withCheckedThrowingContinuation { cont in return try await withCheckedThrowingContinuation { cont in
self.connection.receive(minimumIncompleteLength: 1, maximumLength: 64 * 1024) { data, _, isComplete, error in connection.receive(minimumIncompleteLength: 1, maximumLength: 64 * 1024) { data, _, isComplete, error in
if let error { if let error {
cont.resume(throwing: error) cont.resume(throwing: error)
return return
@ -144,12 +181,17 @@ final class SDLQUICClient {
} }
} }
} }
func stop() { func stop() {
guard self.state != .stopped else {
return
}
self.state = .stopped
self.readTask?.cancel() self.readTask?.cancel()
self.connection.cancel() self.connection?.cancel()
self.eventCont.finish() self.finishMessageContinuationIfNeed(throwing: nil)
self.messageCont.finish()
} }
} }