我必须能够检测到Mac客户端的IP地址更改。我需要在每次换新动作时执行动作,当我从wifi转到有线......
有人做过类似的事吗?我目前每分钟都会进行一次轮询,我需要将其更改为更多事件驱动。
答案 0 :(得分:24)
有多种方法可以执行此操作,从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&#如果您更喜欢阅读113行C ++而不是17行Python,则可以使用C++ sample code。但实际上,这里只有一行对于从未使用过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或相关功能的唯一内容是CFNetwork Programming Guide的导航防火墙部分。但原始技术说明TN1145: Living in a Dynamic TCP/IP Environment仍然存在,并且它有足够的背景和示例代码来弄清楚如何自己编写(以及在收到通知时如何检测新的IP地址)。< / p>
显然,这需要你知道你究竟想要注意什么。如果你不知道,没有人能告诉你如何观察它。您最初的问题是如何检测IP地址变化&#34;。
上面的代码将检测您的默认地址何时更改。这是在将套接字连接到Internet地址而不绑定它时使用的地址,或者将套接字绑定到&#39; 0.0.0.0&#39;充当互联网服务器。如果你还没有编写你关心的服务器代码,那么几乎所有网络客户端都会使用前者,大多数服务器会执行后者,除非你另外配置它们,所以你可能只关心它们。
现在让我们逐一浏览您的评论中的示例:
如果我正在尝试确定网络更改,wifi到LAN
没有从WiFi更改为LAN的事情。当您连接到LAN时,WiFi仍在工作。当然,您可以在连接到LAN之前或之后手动禁用它,但您不必这样做,而且它是一个单独的步骤,并有单独的通知。
通常情况下,添加LAN会将您的默认地址更改为LAN的地址,因此/Network/Global
会通知您。如果操作系统可以判断局域网实际上没有连接到互联网,或者您已经更改了一些隐藏的设置以使其更喜欢WiFi到局域网等,则它不会更改默认地址,并{{ 1}}不会通知你,但你可能不在乎。
如果您关心特定接口是否获取,丢失或更改地址,您可以观看该接口。在大多数Mac上,内置以太网是en0,内置WiFi是en1,当然你可能有第三方USB WiFi接口,或者你可能正在使用系留手机,或者你可能感兴趣LAN中的实际IP地址与LAN连接的VPN的VPN地址不同,等等。如果你正在写一些特殊用途,你可能知道你关心哪个接口,所以你可以观看,例如,/Network/Global
。如果您希望在任何界面更改时收到通知,请注意State:/Network/Interface/en0/IPv4
。
或热点或其他wifi
从一个WiFi网络更改为另一个(热点或其他)将改变en1-或者,如果您正在使用第三方WiFi适配器,则会更改其他界面。如果您当时的默认地址来自WiFi,它也会更改State:/Network/Interface/.*/IPv4
,这意味着上面的代码将按原样运行。如果您的默认地址仍然是您的局域网,Global
不会发生变化,但您可能并不关心。如果您关心,请按上述方式观看Global
或Interface/en1
等。
我应该关注IPV4和6的所有网络设置
只需将IPv4替换为IPv6,或使用Interface/.*
。但你真的关心IPv6吗?
还有什么
你还关心什么?如果您确实想要通知某些内容,那么您可能知道这是什么东西。
除此之外,如果系统告诉您条形界面上的foo地址已更改为&#34; ZZ9 Plural Z Alpha&#34;,并且您从未听说过foo协议,那么你能做什么呢?有用的信息呢?但是如果你真的想要它,那么你可以再次使用正则表达式模式来监视每个界面下的任何内容。