提问人:narner 提问时间:11/16/2023 最后编辑:narner 更新时间:11/19/2023 访问量:123
MacOS Swift 应用程序中不正确的 VPN 数据统计信息
Incorrect VPN Data Statistics in MacOS Swift App
问:
我正在尝试在使用 TunnelKit 创建与 VPN 的 WireGuard 连接的 MacOS Swift 应用程序中获取 Rx(接收的数据包字节数)和 Tx(传输的数据包字节数)。
我已经确认我已连接到我的 VPN 并正在获取数据。 但是,我根本没有取回 Rx 和 Tx 值 - 这是我的代码;我正在使用 SystemConfiguration 框架尝试获取连接的 VPN 的数据。
这是我尝试通过框架获取VPN统计数据的地方SystemConfiguration
#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
}
}
答:
其目的似乎是讨论或审查一段与 VPNConnectionStatisticsManager 类相关的 Objective-C 代码;但是,您的消息中尚未提供实际代码。为了以一种有用的方式进行,我将根据您的描述描述如何实现此类和方法。
在 Objective-C 中,一个名为 VPNConnectionStatisticsManager 的类可能会在头文件 (.h) 中声明,如下所示:
VPNConnectionStatisticsManager.h
#import <Foundation/Foundation.h>
#import <SystemConfiguration/SystemConfiguration.h>
@interface VPNConnectionStatisticsManager : NSObject
- (SCNetworkConnectionRef)initializeVPNConnectionWithServiceID:(NSString \*)serviceID;
@end
实现文件 (.m) 中的实现可能如下所示:
VPNConnectionStatisticsManager.m(VPNConnectionStatisticsManager.m)
#import "VPNConnectionStatisticsManager.h"
@implementation 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;
}
// It is important to define the correct signature for the callback function.
// The actual implementation may log the status change or act upon it.
void ConnectionStatusChanged(SCNetworkConnectionRef connection, SCNetworkConnectionStatus status, void \*info) {
NSLog(@"VPN Connection Status Changed: %d", status);
// Additional logic to handle status change can be implemented here
// ...
}
@end
此基本实现仅演示如何使用 SCNetworkConnectionCreateWithServiceID 函数创建类方法来初始化 VPN 连接,以及如何使用回调函数记录连接状态更改。
请记住:
- 错误检查至关重要,但为了简洁起见,此处省略了错误检查。
- 需要管理返回的 SCNetworkConnectionRef 的生命周期(例如,完成后调用 CFRelease)。
- 根据要求,VPN 状态更改的实际日志记录和处理应该更加可靠。
- 若要在实际应用程序中使用它,应遵循内存管理的最佳实践,尤其是在未使用自动引用计数 (ARC) 的情况下。
如果您有实际代码或有关实现的具体疑虑或问题,请分享以获得更准确和详细的回复。
评论
VPNConnectionStatisticsManager
getVPNData()