87 lines
2.6 KiB
Swift
87 lines
2.6 KiB
Swift
//
|
||
// 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
|
||
}
|
||
|
||
}
|