如何使用SystemConfiguration框架在macOS上检测IPv4地址更改

时间:2019-01-28 21:12:17

标签: macos

我试图在Mac OS上使用SystemConfiguration来在Mac上出现新的网络接口并为其分配新的IP地址时获得通知。

我将其设置为监视系统配置密钥State:/Network/Interface,并且当出现新的网络接口时,我都会收到通知。

但是,只要在新网络接口上分配了IPv4地址(例如通过DHCP),我都希望收到通知。我知道键State:/Network/Interface/en0/IPv4拥有en0接口的IPv4地址。但是,对于所有IPv4地址State:/Network/Interface/.*/IPv4使用手册页中描述的正则表达式不适用于新接口。

我在github上整理了一个很小的最小代码示例,但是也可以使用scutil命令行工具。

Link to demo repository

main.c

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

/* Callback used if a configuration change on monitored keys was detected.
 */
void dynamicStoreCallback(SCDynamicStoreRef store, CFArrayRef changedKeys, void* __nullable info) {
    CFIndex count = CFArrayGetCount(changedKeys);
    for (CFIndex i=0; i<count; i++) {
        NSLog(@"Key \"%@\" was changed", CFArrayGetValueAtIndex(changedKeys, i));
    }
}

int main(int argc, const char * argv[]) {
    NSArray *SCMonitoringInterfaceKeys = @[@"State:/Network/Interface.*"];
    @autoreleasepool {
        SCDynamicStoreRef dsr = SCDynamicStoreCreate(NULL, CFSTR("network_interface_detector"), &dynamicStoreCallback, NULL);
        SCDynamicStoreSetNotificationKeys(dsr, CFBridgingRetain(SCMonitoringInterfaceKeys), NULL);
        CFRunLoopAddSource(CFRunLoopGetCurrent(), SCDynamicStoreCreateRunLoopSource(NULL, dsr, 0), kCFRunLoopDefaultMode);
        NSLog(@"Starting RunLoop...");
        while([[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]);
    }
    return 0;
}

1 个答案:

答案 0 :(得分:0)

在一些开发人员同事的帮助下,我找出了问题所在。 SCDynamicStoreSetNotificationKeys函数的签名如下:

Boolean SCDynamicStoreSetNotificationKeys (SCDynamicStoreRef store,
                CFArrayRef          __nullable  keys,
                CFArrayRef          __nullable  patterns
                )

这意味着我必须与用作树根的键分开设置模式,在树的根下将进行模式匹配。这是我的 main.m 的修改版本:

int main(int argc, const char * argv[]) {
    NSArray *SCMonitoringInterfaceKeys = @[@"State:/Network/Interface"];
    NSArray *patterns = @[@"en\\d*/IPv4"];
    @autoreleasepool {
        SCDynamicStoreRef dsr = SCDynamicStoreCreate(NULL, CFSTR("network_interface_detector"), &dynamicStoreCallback, NULL);
        SCDynamicStoreSetNotificationKeys(dsr, CFBridgingRetain(SCMonitoringInterfaceKeys), CFBridgingRetain(patterns));
        CFRunLoopAddSource(CFRunLoopGetCurrent(), SCDynamicStoreCreateRunLoopSource(NULL, dsr, 0), kCFRunLoopDefaultMode);
        NSLog(@"Starting RunLoop...");
        while([[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]);
    }
    return 0;
}

我已经包含了解决方案into the solved branch of the repo