// // SDLPuncherActor.swift // Tun // // Created by 安礼成 on 2026/1/7. // import Foundation import NIOCore actor SDLPuncherActor { // 10秒内只需要提交一次查询 nonisolated private let cooldownInterval: TimeInterval = 10 // 等待peerInfo返回的超时时间 nonisolated private let peerInfoTimeout: TimeInterval = 3 struct RegisterRequest { let srcMac: Data let dstMac: Data let networkId: UInt32 } private enum RequestPhase { case waitingPeerInfo(deadline: Date) case coolingDown } private struct RequestEntry { let request: RegisterRequest let cooldownUntil: Date var phase: RequestPhase func canSubmit(at now: Date) -> Bool { return cooldownUntil <= now } func isWaitingPeerInfo(at now: Date) -> Bool { guard case .waitingPeerInfo(let deadline) = self.phase else { return false } return deadline > now } mutating func markCoolingDown() { self.phase = .coolingDown } } // dstMac private var requestEntries: [Data: RequestEntry] = [:] func runCleanup() async throws { while !Task.isCancelled { try await Task.sleep(for: .seconds(1)) try Task.checkCancellation() self.cleanupExpiredEntries() } } func makeQueryInfoRequest(request: RegisterRequest) async -> Data? { let now = Date() self.cleanupExpiredEntries(now: now) if let entry = self.requestEntries[request.dstMac], !entry.canSubmit(at: now) { return nil } var queryInfo = SDLQueryInfo() queryInfo.dstMac = request.dstMac guard let queryData = try? queryInfo.serializedData() else { SDLLogger.log("[SDLPuncherActor] failed to encode queryInfo", category: .session) return nil } self.requestEntries[request.dstMac] = RequestEntry( request: request, cooldownUntil: now.addingTimeInterval(self.cooldownInterval), phase: .waitingPeerInfo(deadline: now.addingTimeInterval(self.peerInfoTimeout)) ) return queryData } func makeRegisterPackets(peerInfo: SDLPeerInfo) async -> [(data: Data, remoteAddress: SocketAddress)] { let now = Date() self.cleanupExpiredEntries(now: now) guard var entry = self.requestEntries[peerInfo.dstMac], entry.isWaitingPeerInfo(at: now) else { return [] } entry.markCoolingDown() self.requestEntries[peerInfo.dstMac] = entry var register = SDLRegister() register.networkID = entry.request.networkId register.srcMac = entry.request.srcMac register.dstMac = entry.request.dstMac guard let registerData = try? register.serializedData() else { SDLLogger.log("[SDLPuncherActor] failed to encode register", category: .session) return [] } var packets: [(data: Data, remoteAddress: SocketAddress)] = [] if peerInfo.hasV4Info, let remoteAddress = try? await peerInfo.v4Info.socketAddress() { packets.append((data: registerData, remoteAddress: remoteAddress)) } if peerInfo.hasV6Info, let remoteAddress = try? await peerInfo.v6Info.socketAddress() { packets.append((data: registerData, remoteAddress: remoteAddress)) } return packets } func stop() { self.requestEntries.removeAll() } private func cleanupExpiredEntries(now: Date = Date()) { self.requestEntries = self.requestEntries.filter { _, entry in !entry.canSubmit(at: now) } } }