MacOS Swift 应用程序中的 VPN 数据统计不正确

问题描述 投票:0回答:1

我正在尝试在 MacOS Swift 应用程序中获取 Rx(接收的数据包字节计数)和 Tx(传输的数据包字节计数),该应用程序使用 TunnelKit 创建与 VPN 的 WireGuard 连接。

我已确认我已连接到 VPN 并且正在获取数据。 但是,我根本没有得到 Rx 和 Tx 值 - 这是我的代码;我正在使用 SystemConfiguration 框架来尝试获取已连接 VPN 的数据。

这是我尝试通过

SystemConfiguration
框架获取 VPN 统计数据的地方

#import <Foundation/Foundation.h>
#import <SystemConfiguration/SystemConfiguration.h>

NS_ASSUME_NONNULL_BEGIN

@interface VPNConnectionStatisticsManager : NSObject

- (SCNetworkConnectionRef _Nullable)initializeVPNConnectionWithServiceID:(NSString *)serviceID;

@end

NS_ASSUME_NONNULL_END


#import <Foundation/Foundation.h>
#import "VPNConnectionStatisticsManager.h"

@implementation VPNConnectionStatisticsManager

- (SCNetworkConnectionRef _Nullable)initializeVPNConnectionWithServiceID:(NSString *)serviceID {
    SCNetworkConnectionContext context = {0, NULL, NULL, NULL, NULL};
    SCNetworkConnectionRef connection = SCNetworkConnectionCreateWithServiceID(NULL, (__bridge CFStringRef)serviceID, ConnectionStatusChanged, &context);
    return connection;
}


static void ConnectionStatusChanged(SCNetworkConnectionRef connection, SCNetworkConnectionStatus status, void *info) {
    NSLog(@"VPN status changed: %d", (int)status);
}

@end

我更改了代码以确保不会在每次调用时重新创建

VPNConnectionStatisticsManager
。还包括我的视图代码,在上下文中显示此内容:

class VPNStatusViewModel: ObservableObject {
    var vpnStatusRefreshTimer: Timer?
    var vpnConnection: SCNetworkConnection?
    private let vpnConnectionStatisticsManager = VPNConnectionStatisticsManager()

    func initializeVPNConnection() {
        if let serviceID = findMeterVPNServiceID(),
           let vpnConnectionRef = vpnConnectionStatisticsManager.initializeVPNConnection(withServiceID: serviceID) {
            vpnConnection = vpnConnectionRef.takeRetainedValue()
        }
    }

    func getVPNData() -> (rx: String, tx: String) {
        var rx: String = "N/A"
        var tx: String = "N/A"
        
        if let vpnConnection = vpnConnection,
           let statsDict = SCNetworkConnectionCopyStatistics(vpnConnection) as? [String: Any],
           let vpnDict = statsDict["VPN"] as? [String: Any] {
            if let rxInt = vpnDict["BytesIn"] as? Int, let txInt = vpnDict["BytesOut"] as? Int {
                let formatter = ByteCountFormatter()
                formatter.allowedUnits = [.useBytes, .useKB, .useMB]
                rx = formatter.string(fromByteCount: Int64(rxInt))
                tx = formatter.string(fromByteCount: Int64(txInt))
            }
        }
        
        return (rx, tx)
    }

    func stopVPNStatusTimer() {
        vpnStatusRefreshTimer?.invalidate()
        vpnStatusRefreshTimer = nil
    }

    func releaseVPNConnection() {
        vpnConnection = nil
    }
}

struct VPNStatusPane: View {
    @ObservedObject var appDelegate: AppDelegate
    @StateObject private var viewModel = VPNStatusViewModel()
    @State var connectedSince: String
    @State var rxBytes: String = "0"
    @State var txBytes: String = "0"

    init(appDelegate: AppDelegate) {
        self.appDelegate = appDelegate
        self._connectedSince = State(initialValue: appDelegate.vpnConnectedSince)
    }

    var body: some View {
        VStack(alignment: .leading) {
            VStack(alignment: .leading, spacing: 10) {
                HypeneatedText(label: "Rx bytes", value: rxBytes)
                HypeneatedText(label: "Tx bytes", value: txBytes)
                HypeneatedText(label: "Connected for", value: connectedSince)
            }
            .padding(.top, 15)

            Spacer()
        }
        .onAppear {
            viewModel.initializeVPNConnection()
            updateVPNData()
            startVPNStatusTimer()
        }
        .onDisappear {
            viewModel.stopVPNStatusTimer()
            viewModel.releaseVPNConnection()
        }
        .frame(maxWidth: .infinity, alignment: .topLeading)
    }

    private func startVPNStatusTimer() {
        viewModel.vpnStatusRefreshTimer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { [self] _ in
            if self.appDelegate.vpnIsConnected {
                self.appDelegate.updateConnectionTime()
            }
            self.connectedSince = self.appDelegate.vpnConnectedSince
            self.updateVPNData()
        }
    }

    private func updateVPNData() {
        let vpnData = viewModel.getVPNData()
        rxBytes = vpnData.rx
        txBytes = vpnData.tx
    }
}
swift macos system wireguard system-configuration
1个回答
0
投票

看起来目的是讨论或回顾一段与 VPNConnectionStatisticsManager 类相关的 Objective-C 代码;但是,您的消息中尚未提供实际的代码。为了以有用的方式继续,我将根据您的描述描述如何实现这样的类和方法。

在 Objective-C 中,名为 VPNConnectionStatisticsManager 的类可能会在头文件 (.h) 中声明,如下所示:

// VPNConnectionStatisticsManager.h

#导入

#导入

@接口VPNConnectionStatisticsManager:NSObject

- (SCNetworkConnectionRef)initializeVPNConnectionWithServiceID:(NSString *)serviceID;

@结束

实现文件(.m)中的实现可能如下:

// VPNConnectionStatisticsManager.m

#import“VPNConnectionStatisticsManager.h”

@实施VPNConnectionStatisticsManager

- (SCNetworkConnectionRef)initializeVPNConnectionWithServiceID:(NSString *)serviceID {

// Assume that serviceID is already a valid and properly formatted string.

CFStringRef serviceIDRef = (\__bridge CFStringRef)(serviceID);

SCNetworkConnectionContext context = {0, (\__bridge void \*)(self), NULL, NULL, NULL};

SCNetworkConnectionRef connectionRef = SCNetworkConnectionCreateWithServiceID(NULL, serviceIDRef, ConnectionStatusChanged, \&context);



// Here, you might want to add additional setup if necessary

// ...



return connectionRef;

}

// 为回调函数定义正确的签名非常重要。

// 实际实现可能会记录状态更改或对其采取行动。

void ConnectionStatusChanged(SCNetworkConnectionRef 连接,SCNetworkConnectionStatus 状态,void *info){

NSLog(@"VPN Connection Status Changed: %d", status);



// Additional logic to handle status change can be implemented here

// ...

}

@结束

此基本实现简单演示了如何创建类方法来使用 SCNetworkConnectionCreateWithServiceID 函数初始化 VPN 连接,以及如何使用回调函数记录连接状态更改。

请记住:

  • 错误检查至关重要,但为了简洁起见,此处省略。
  • 您需要管理返回的 SCNetworkConnectionRef 的生命周期(例如,完成后调用 CFRelease)。
  • VPN 状态更改的实际记录和处理应该更加稳健,具体取决于要求。
  • 要在实际应用程序中使用它,您应该遵循内存管理的最佳实践,特别是如果您不使用自动引用计数 (ARC)。

如果您有实际代码或有关实施的具体疑虑或问题,请分享以获得更准确和详细的答复。

© www.soinside.com 2019 - 2024. All rights reserved.