如何在C或C++中以编程方式检测OSX上的IP地址更改
How to detect IP address change on OSX programmatically in C or C++
我必须能够检测到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/en1
或Interface/.*
等,如上所述。
对于IPV4和6 ,我应该关注哪些网络设置
只需将IPv4替换为IPv6,或者使用IPv.
。但是你真的关心IPv6吗?
还有什么
您还关心什么?如果有什么东西你真的想要通知,你大概知道它是什么
除此之外,如果系统告诉您bar界面上的foo地址已更改为"ZZ9 Plural Z Alpha",而您从未听说过foo协议,那么您对这些信息有什么用处?但是,如果您真的想要它,同样,您可以使用regex模式来监视每个接口下的任何内容。
- OSX Catalina 上的C++和宝石问题
- Xcode OSX上的C++构建失败,出现多个错误文件IO..不可用:在macOS 10.15中引入
- 让constexpr在OSX上的C++17中使用pow
- 如何在Mac OSX上的Qt中使用pkcs12 / pfx成功执行SSL加密?
- 为什么异常不适用于 OSX 上的 gcc7 和 -static-libgcc?
- 构建共享对象 - 使用 Ninja 编译适用于 OSX 和 Windows 上的 Ubuntu Crush
- OSX 上的 Apache Ignite 2.0 C++ 无法编译
- OSX 上的 GCC 5、6 和 7 不支持 uint
- 如何在Mac OSX上的窗口客户端内获取真实的鼠标位置?
- 代码调用反向函数不会在Ubuntu 18上的G 或Clang 上编译,但神秘地在Mac OSX上使用
- OSX上的Gldrawelements具有很高的CPU使用情况
- 如何在OSX上的C 中编程旋转光标
- OSX 10.8.2 上的 Poco C++库:体系结构x86_64的未定义符号
- osX上的c++未捕获异常
- Macports GCC 4.8无法链接OSX Lion上的c++库
- OSX 优胜美地 10.10.3 上的 Cmake - GLEW:未找到包'gl'
- OSX 上的应用程序构建找不到配置文件
- 将osx上的默认编译器从llvm-g++更改为g++
- 引发异常导致OSX 10.11.4+clang上的SIGSEGV
- OSX 上的 Boost 和 C++ 链接器错误