如何在C或C ++中以编程方式检测OSX上的IP地址更改

我必须能够检测到我的Mac客户端的IP地址更改。 我需要在每次换新动作时执行动作,当我从wifi转到有线时…

有人做过类似的事吗? 我目前每分钟都会进行一次调查,我需要将其更改为更多事件驱动。

从IOKit通知开始,有多种方法可以做到这一点,但最简单的可能是SystemConfiguration框架。

第一步是启动scutil并使用它来确定要通知的键:

$ scutil > list ... > n.add State:/Network/Global/IPv4 > n.watch ... unplug your network cable (or disconnect from WiFi) notification callback (store address = 0x10e80e3c0). changed key [0] = State:/Network/Global/IPv4 

看看那个,先试试吧。 :)但是如果你想观看一个特定的网卡,或者使用IPv6代替v4等,显然你需要一个不同的密钥来自列表。 请注意,您可以使用正则表达式模式(POSIX样式,由man 3 regex定义),因此如果您想要观看任何用于IPv4的NIC,您可以使用State:/Network/Interface/.*/IPv4 ,或者如果你想说全球IPv4或IPv6, State:/Network/Global/IPv.

现在,您只需使用所需的键调用SCDynamicStoreSetNotificationKeys。

请注意,SCDynamicStoreSetNotificationKeys可以采用正则表达式模式(POSIX样式,由man 3正则表达式定义)

因为它在C中有点痛苦,我会用Python编写它:

 #!/usr/bin/python from Foundation import * from SystemConfiguration import * def callback(store, keys, info): for key in keys: print key, SCDynamicStoreCopyValue(store, key) store = SCDynamicStoreCreate(None, "global-network-watcher", callback, None) SCDynamicStoreSetNotificationKeys(store, None, ['State:/Network/Global/IPv4']) CFRunLoopAddSource(CFRunLoopGetCurrent(), SCDynamicStoreCreateRunLoopSource(None, store, 0), kCFRunLoopCommonModes) CFRunLoopRun() 

这在C中更加痛苦的主要原因是你需要几十行样板来创建一个带有CFString的CFArray,打印CFString值,管理对象的生命周期等等。从Jeremy Friesner的评论来看,有C ++如果您宁愿阅读113行C ++而不是17行Python,那么可以使用示例代码 。 但实际上,这里只有一行对于从未使用过Python的人来说应该是不熟悉的:

 def callback(store, keys, info): for key in keys: print key, SCDynamicStoreCopyValue(store, key) 

……相当于C定义:

 void callback(SCDynamicStoreRef store, CFArrayRef keys, void *info) { /* iterate over keys, printing something for each one */ } 

奇怪的是,我再也找不到SystemConfiguration的实际参考或指南文档; SCDynamicStoreSetNotificationKeys或相关function的唯一内容是在“ CFNetwork编程指南”的“导航防火墙”部分中。 但原始技术说明TN1145:生活在动态TCP / IP环境中仍然存在,并且它有足够的背景和示例代码来弄清楚如何自己编写(以及如何在收到通知时检测新的IP地址)。

显然,这需要你知道你究竟想要注意什么。 如果你不知道,没有人能告诉你如何观察它。 您最初的问题是如何“检测IP地址更改”。

上面的代码将检测您的默认地址何时更改。 这是在将套接字连接到Internet地址而不绑定它时使用的地址,或者将套接字绑定到’0.0.0.0’以充当Internet服务器。 如果您还没有编写您关心的服务器代码,几乎所有网络客户端都会执行前者,大多数服务器都会执行后者,除非您另外配置它​​们,因此您可能只关心它们。

现在让我们逐一介绍你的评论中的例子:

如果我正在尝试确定网络更改,wifi到局域网

没有从WiFi变为LAN的事情。 当您连接到LAN时,WiFi仍在工作。 当然,您可以在连接到LAN之前或之后手动禁用它,但您不必这样做,而且这是一个单独的步骤,并带有单独的通知。

通常,添加LAN会将您的默认地址更改为LAN的地址,因此/Network/Global会通知您。 如果操作系统可以告诉局域网实际上没有连接到互联网,或者您已经更改了一些隐藏的设置以使其更喜欢WiFi到局域网等,它将不会更改默认地址,并且/Network/Global不会通知你,但你可能不在乎。

如果您确实关心特定接口是否获取,丢失或更改地址,您可以观看该接口。 在大多数Mac上,内置以太网是en0,内置WiFi是en1,但当然你可能有第三方USB WiFi接口,或者你可能正在使用系留手机,或者你可能感兴趣LAN的实际IP地址与局域网所连接的VPN的VPN地址不同,等等。如果你正在写一些特殊用途,你可能知道你关心哪个接口,所以你可以看,例如, State:/Network/Interface/en0/IPv4 。 如果您希望在任何界面更改时收到通知,请查看State:/Network/Interface/.*/IPv4

或热点或其他wifi

从一个WiFi网络更改为另一个(热点或其他)将改变en1-或者,如果您正在使用第三方WiFi适配器,则会更改其他接口。 如果您当时的默认地址来自WiFi,它也会更改Global ,这意味着上面的代码将按原样运行。 如果您的默认地址仍然是您的LAN,则Global不会更改,但您可能不在乎。 如果您关心,请按上述方式观看Interface/en1Interface/.*等。

我应该为IPV4和6观看所有网络设置

只需用IPv4替换IPv4,或使用IPv IPv. 。 但你真的关心IPv6吗?

还有什么

你还关心什么? 如果您确实想要通知某些内容,那么您可能知道这是什么。

除此之外,如果系统告诉您条形界面上的foo地址已更改为“ZZ9 Plural Z Alpha”,并且您从未听说过foo协议,那么您可以对该信息做些什么? 但是如果你真的想要它,那么你可以再次使用正则表达式模式来监视每个界面下的任何内容。