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