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

rez*_*eza 16 c c++ macos

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

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

aba*_*ert 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
Run Code Online (Sandbox Code Playgroud)

看看那个,先试试吧.:)但是如果你想观看一个特定的网卡,或者使用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()
Run Code Online (Sandbox Code Playgroud)

这在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)
Run Code Online (Sandbox Code Playgroud)

......相当于C定义:

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

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

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

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

现在让我们逐一查看您的评论中的示例:

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

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

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

如果您确实关心特定接口是否获取,丢失或更改地址,您可以观察该接口.在大多数Mac上,内置以太网是en0,内置WiFi是en1,当然你可能有第三方USB WiFi接口,或者你可能正在使用系留手机,或者你可能感兴趣LAN的实际IP地址与LAN连接的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..但你真的关心IPv6吗?

还有什么

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

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

  • 在http://www.lcscanada.com/jaf/osx_ip_change_notify.cpp(源自Apple的技术说明代码)中有一个简单但可运行的C/C++示例程序...用"g ++ osx_ip_change_notify.cpp -framework"编译它SystemConfiguration -framework Carbon" (4认同)
  • 要在 scutil 中使用模式,请使用以下语法:`n.add <key pattern> "pattern"` 示例 `n.add State:/Network/Global/.* "pattern"` (2认同)