// // SDLPuncherActor.swift // Tun // // Created by 安礼成 on 2026/1/7. // import Foundation import NIOCore actor SDLPuncherActor { // 10秒内只需要提交一次查询 nonisolated private let cooldownInterval: TimeInterval = 10 struct RequestContext { let expireAt: Date let request: RegisterRequest func isExpired() -> Bool { return expireAt < Date() } } // dstMac private var pendingRequests: [Data: RequestContext] = [:] struct RegisterRequest { let srcMac: Data let dstMac: Data let networkId: UInt32 } func submitRegisterRequest(quicClient: SDLQUICClient?, request: RegisterRequest) { guard let quicClient else { return } // 数据不存在,或者已经过期;才能提交 let dstMac = request.dstMac guard self.isRequestExpired(dstMac: dstMac) else { return } self.pendingRequests[dstMac] = .init(expireAt: Date().addingTimeInterval(cooldownInterval), request: request) // 触发一次打洞 var queryInfo = SDLQueryInfo() queryInfo.dstMac = request.dstMac if let queryData = try? queryInfo.serializedData() { quicClient.send(type: .queryInfo, data: queryData) } } func handlePeerInfo(using udpHole: SDLUDPHole?, peerInfo: SDLPeerInfo) async { // 如果服务器返回了值,优先删除掉; 避免数据堆积 guard let requestContext = pendingRequests.removeValue(forKey: peerInfo.dstMac) else { return } // 判断必要的值是否存在 guard let udpHole, peerInfo.hasV4Info else { return } if let remoteAddress = try? await peerInfo.v4Info.socketAddress() { SDLLogger.shared.log("[SDLContext] hole sock address: \(remoteAddress)", level: .debug) // 发送register包 var register = SDLRegister() register.networkID = requestContext.request.networkId register.srcMac = requestContext.request.srcMac register.dstMac = requestContext.request.dstMac if let registerData = try? register.serializedData() { udpHole.send(type: .register, data: registerData, remoteAddress: remoteAddress) } } } // 判断是否需要提交 func isRequestExpired(dstMac: Data) -> Bool { if let context = pendingRequests[dstMac] { return context.isExpired() } return true } }