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

How to detect IP address change on OSX programmatically in C or C++

本文关键字:上的 OSX 检测 IP 地址 方式 编程 C++      更新时间:2023-10-16

我必须能够检测到Mac客户端的IP地址更改。每次我得到一个新的,当我从无线网络到有线网络时,我都需要执行一个动作。。。

有人做过类似的事情吗?我目前每分钟都在进行民意调查,我需要改变这一点,使其更受事件驱动。

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

第一步是启动盾并使用它来确定您想要通知的密钥:

$ 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

看看这个,第一次尝试就成功了。:)但是,如果你想观看特定的NIC,或者使用IPv6而不是v4等,显然你会想要列表中的不同密钥。请注意,您可以使用正则表达式模式(POSIX样式,由man 3 regex定义),因此,如果您想查看IPv4的任何NIC,则可以使用State:/Network/Interface/.*/IPv4,或者如果您想了解全局IPv4或IPv6、State:/Network/Global/IPv.等。

现在您只需使用所需的密钥调用SCDynamicStoreSetNotificationKeys。

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

由于它在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++示例代码。但实际上,对于从未使用过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编程指南》的"导航防火墙"部分。但最初的技术专家TN1145:生活在动态TCP/IP环境中仍然存在,它有足够的背景和示例代码来了解如何自己编写(以及如何在收到通知时检测新的IP地址)。

显然,这需要你知道你到底想看什么。如果你不知道,没有人能告诉你如何监视它。你最初的问题是如何"检测IP地址更改"。

上面的代码将检测您的默认地址何时更改。当你将套接字连接到互联网地址而不绑定它时,或者将套接字绑定到"0.0.0.0"以充当互联网服务器时,就会使用这个地址。如果你还没有编写你关心的服务器代码,那么几乎所有的网络客户端都会编写前者,大多数服务器都会编写后者,除非你对它们进行了其他配置,所以这可能就是你关心的全部。

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

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

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

通常,添加LAN会将您的默认地址更改为LAN的地址,因此/Network/Global会通知您。如果操作系统可以判断LAN实际上没有连接到互联网,或者你更改了一些隐藏设置,使其更喜欢WiFi而不是LAN等,它不会更改默认地址,/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,这意味着上面的代码将按原样工作。如果您的默认地址仍然是您的局域网,Global不会更改,但您可能不在乎。如果您真的很在意,请观看Interface/en1Interface/.*等,如上所述。

对于IPV4和6 ,我应该关注哪些网络设置

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

还有什么

您还关心什么?如果有什么东西你真的想要通知,你大概知道它是什么

除此之外,如果系统告诉您bar界面上的foo地址已更改为"ZZ9 Plural Z Alpha",而您从未听说过foo协议,那么您对这些信息有什么用处?但是,如果您真的想要它,同样,您可以使用regex模式来监视每个接口下的任何内容。