// // SDLPuncherActor.swift // Tun // // Created by 安礼成 on 2026/1/7. // import Foundation import NIOCore actor SDLPuncherActor { nonisolated private let cooldown: Duration = .seconds(5) // dstMac private var coolingDown: Set = [] private var pktId: UInt32 = 1 // 提交后还没有响应的请求 private var pendingRequests: [UInt32: RegisterRequest] = [:] // 处理holer nonisolated private let querySocketAddress: SocketAddress struct RegisterRequest { let srcMac: Data let dstMac: Data let networkId: UInt32 } init(querySocketAddress: SocketAddress) { self.querySocketAddress = querySocketAddress } func submitRegisterRequest(quicClient: SDLQUICClient?, request: RegisterRequest) { let dstMac = request.dstMac guard let quicClient, !coolingDown.contains(dstMac) else { return } // 触发一次打洞 coolingDown.insert(dstMac) let pktId = self.pktId self.pktId &+= 1 if self.pktId == 0 { self.pktId = 1 } self.tryHole(using: quicClient, pktId: pktId, request: request) Task { // 启动冷却期 try? await Task.sleep(for: .seconds(5)) self.endCooldown(for: dstMac) self.removePendingRequest(for: pktId) } } func handlePeerInfo(using udpHole: SDLUDPHole, peerInfo: SDLPeerInfo) async { guard let request = pendingRequests.removeValue(forKey: peerInfo.pktID), let remoteAddress = try? await peerInfo.v4Info.socketAddress() else { return } SDLLogger.shared.log("[SDLContext] hole sock address: \(remoteAddress)", level: .debug) // 发送register包 var register = SDLRegister() register.networkID = request.networkId register.srcMac = request.srcMac register.dstMac = request.dstMac if let registerData = try? register.serializedData() { udpHole.send(type: .register, data: registerData, remoteAddress: remoteAddress) } } private func endCooldown(for key: Data) { self.coolingDown.remove(key) } private func removePendingRequest(for pktId: UInt32) { self.pendingRequests.removeValue(forKey: pktId) } private func tryHole(using quicClient: SDLQUICClient, pktId: UInt32, request: RegisterRequest) { var queryInfo = SDLQueryInfo() queryInfo.pktID = pktId queryInfo.dstMac = request.dstMac self.pendingRequests[pktId] = request if let queryData = try? queryInfo.serializedData() { quicClient.send(type: .queryInfo, data: queryData) } } }