fix(client/macos): StatsCollector 用独立 client 订阅 Group,修 urltest 延迟为空
诊断确认:连接页延迟显示 — 是因为扩展回给主 app 的 urltest 数组恒为空(speeds 正常)。 根因:libbox command 协议是「一连接一命令」,把 Status 与 Group addCommand 到同一 client 只有 Status 生效,Group 永不回调 → 无 urltest。对齐 sing-box-for-apple:每命令各开一个 client。新增 groupClient 专订 Group + 跑 urlTest,延迟即随统计帧回到连接页(与 Windows 同源)。 扩展代码变更 → CURRENT_PROJECT_VERSION 45→46,让 sysextd 重装。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -344,7 +344,11 @@ private func simpleError(_ msg: String) -> NSError {
|
||||
// 主 app 端 StatsClient,但跑在扩展里——根治主 app 跨 root/用户容器连不上 socket 的死局。
|
||||
final class StatsCollector: NSObject {
|
||||
private let queue = DispatchQueue(label: "pangolin.stats.collector")
|
||||
private var client: LibboxCommandClient?
|
||||
// ⚠️ libbox 的 command 协议是「一连接一命令」:一个 client 只订阅一种命令。把 Status 与
|
||||
// Group addCommand 到同一 client,只有第一种(Status)生效,Group 永不回调 → urltest 为空、
|
||||
// 连接页延迟显示 —。对齐 sing-box-for-apple:每种命令各开一个 client。
|
||||
private var statusClient: LibboxCommandClient?
|
||||
private var groupClient: LibboxCommandClient?
|
||||
private var started = false
|
||||
private var urlTestTimer: DispatchSourceTimer?
|
||||
|
||||
@@ -362,37 +366,44 @@ final class StatsCollector: NSObject {
|
||||
func start() {
|
||||
queue.async { [weak self] in
|
||||
guard let self, !self.started else { return }
|
||||
let options = LibboxCommandClientOptions()
|
||||
options.statusInterval = 1_000_000_000 // 1s
|
||||
options.addCommand(LibboxCommandStatus)
|
||||
options.addCommand(LibboxCommandGroup)
|
||||
guard let c = LibboxNewCommandClient(self, options) else {
|
||||
NSLog("[pangolin/collector] LibboxNewCommandClient nil")
|
||||
return
|
||||
}
|
||||
// command.sock 刚由 server.start() 建好,可能稍滞后,重试到连上。
|
||||
for _ in 0..<20 {
|
||||
do {
|
||||
try c.connect()
|
||||
self.client = c
|
||||
self.started = true
|
||||
self.startUrlTestTimer()
|
||||
NSLog("[pangolin/collector] connected to own command.sock")
|
||||
return
|
||||
} catch {
|
||||
Thread.sleep(forTimeInterval: 0.25)
|
||||
}
|
||||
}
|
||||
NSLog("[pangolin/collector] gave up connecting to own command.sock")
|
||||
self.statusClient = self.connectClient(command: LibboxCommandStatus, label: "status")
|
||||
self.groupClient = self.connectClient(command: LibboxCommandGroup, label: "group")
|
||||
guard self.statusClient != nil || self.groupClient != nil else { return }
|
||||
self.started = true
|
||||
self.startUrlTestTimer()
|
||||
}
|
||||
}
|
||||
|
||||
/// 为单一命令开一个 client 并连上(command.sock 刚建好可能稍滞后,重试)。
|
||||
private func connectClient(command: Int32, label: String) -> LibboxCommandClient? {
|
||||
let options = LibboxCommandClientOptions()
|
||||
options.statusInterval = 1_000_000_000 // 1s
|
||||
options.addCommand(command)
|
||||
guard let c = LibboxNewCommandClient(self, options) else {
|
||||
NSLog("[pangolin/collector] %@ LibboxNewCommandClient nil", label)
|
||||
return nil
|
||||
}
|
||||
for _ in 0..<20 {
|
||||
do {
|
||||
try c.connect()
|
||||
NSLog("[pangolin/collector] %@ connected", label)
|
||||
return c
|
||||
} catch {
|
||||
Thread.sleep(forTimeInterval: 0.25)
|
||||
}
|
||||
}
|
||||
NSLog("[pangolin/collector] %@ gave up connecting", label)
|
||||
return nil
|
||||
}
|
||||
|
||||
func stop() {
|
||||
queue.async { [weak self] in
|
||||
guard let self else { return }
|
||||
self.urlTestTimer?.cancel(); self.urlTestTimer = nil
|
||||
if let c = self.client { try? c.disconnect() }
|
||||
self.client = nil
|
||||
if let c = self.statusClient { try? c.disconnect() }
|
||||
if let c = self.groupClient { try? c.disconnect() }
|
||||
self.statusClient = nil
|
||||
self.groupClient = nil
|
||||
self.started = false
|
||||
self.lock.lock()
|
||||
self.latest = StatsCollector.zero
|
||||
@@ -410,9 +421,10 @@ final class StatsCollector: NSObject {
|
||||
private func startUrlTestTimer() {
|
||||
urlTestTimer?.cancel()
|
||||
let timer = DispatchSource.makeTimerSource(queue: queue)
|
||||
timer.schedule(deadline: .now() + 2, repeating: 12)
|
||||
timer.schedule(deadline: .now() + 2, repeating: 10)
|
||||
timer.setEventHandler { [weak self] in
|
||||
guard let self, let c = self.client else { return }
|
||||
guard let self, let c = self.groupClient else { return }
|
||||
// groupTags 由 writeGroups 回调填充;主动触发各组 urltest,补偿 sing-box 惰性探测。
|
||||
for tag in self.groupTags { try? c.urlTest(tag) }
|
||||
}
|
||||
timer.resume()
|
||||
|
||||
@@ -659,7 +659,7 @@
|
||||
baseConfigurationReference = C57BBFD43F9175D1E23685EF /* Pods-RunnerTests.debug.xcconfig */;
|
||||
buildSettings = {
|
||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||
CURRENT_PROJECT_VERSION = 45;
|
||||
CURRENT_PROJECT_VERSION = 46;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
MARKETING_VERSION = 1.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.pangolin.pangolin.RunnerTests;
|
||||
@@ -674,7 +674,7 @@
|
||||
baseConfigurationReference = F8904897A48DC81799B8752E /* Pods-RunnerTests.release.xcconfig */;
|
||||
buildSettings = {
|
||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||
CURRENT_PROJECT_VERSION = 45;
|
||||
CURRENT_PROJECT_VERSION = 46;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
MARKETING_VERSION = 1.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.pangolin.pangolin.RunnerTests;
|
||||
@@ -689,7 +689,7 @@
|
||||
baseConfigurationReference = B21E68FC1F5D33DD67A0DF5E /* Pods-RunnerTests.profile.xcconfig */;
|
||||
buildSettings = {
|
||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||
CURRENT_PROJECT_VERSION = 45;
|
||||
CURRENT_PROJECT_VERSION = 46;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
MARKETING_VERSION = 1.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.pangolin.pangolin.RunnerTests;
|
||||
@@ -960,7 +960,7 @@
|
||||
CODE_SIGN_ENTITLEMENTS = PacketTunnel/PacketTunnel.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 45;
|
||||
CURRENT_PROJECT_VERSION = 46;
|
||||
DEVELOPMENT_TEAM = BYL4KQHMTN;
|
||||
ENABLE_APP_SANDBOX = YES;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
@@ -1010,7 +1010,7 @@
|
||||
CODE_SIGN_ENTITLEMENTS = PacketTunnel/PacketTunnel.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Developer ID Application";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
CURRENT_PROJECT_VERSION = 45;
|
||||
CURRENT_PROJECT_VERSION = 46;
|
||||
DEVELOPMENT_TEAM = BYL4KQHMTN;
|
||||
ENABLE_APP_SANDBOX = YES;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
@@ -1059,7 +1059,7 @@
|
||||
CODE_SIGN_ENTITLEMENTS = PacketTunnel/PacketTunnel.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 45;
|
||||
CURRENT_PROJECT_VERSION = 46;
|
||||
DEVELOPMENT_TEAM = BYL4KQHMTN;
|
||||
ENABLE_APP_SANDBOX = YES;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
|
||||
Reference in New Issue
Block a user