实时注册网络适配器变化

Registering network adapter changes in real time

对于 Python 应用程序,我想尽快对网络适配器的变化做出反应,例如每当适配器连接或断开连接或任何适配器的 IP 地址更改时。我在 Windows 10.

我目前的方法是使用 win32evtlog 模块中的 EvtSubscribe 来监视事件日志 Microsoft-Windows-NetworkProfile/Operational 中的新条目。但是,IP 地址更改似乎需要 1 到 10 秒才能以这种方式注册,并且 connecting/disconnecting 适配器比 Control Panel\Network and Internet\Network Connections.[=15 中图标上的反应时间慢大约一秒=]

有什么方法可以让我更快地对这些变化做出反应?我也欢迎解释为什么更快的反应 是可能的。

我自己发现并尝试了三种方法,如下所示。请注意,它们都是 Windows 特定的。我测试了通过插入以太网电缆 in/out 以及 enabling/disabling WLAN 连接的网络适配器的更改。连接适配器总是需要一段时间才能注册,Control Panel\Network and Internet\Network Connections 中的 Windows GUI 也需要大约三秒钟。我想在那里没什么可做的。

网络变化

在 .NET API 中使用 System.Net.NetworkInformation 中的 pythonnet allows importing NetworkChangeNetworkAddressChanged事件可用于通知地址适配器连接信息。 NetworkAvailabilityChanged 事件只会在没有适配器连接到网络时触发,这可能是也可能不是您想要的。 这种方法总体上给了我最好的结果:地址更改和适配器断开连接需要不到一秒的时间来注册,连接适配器大约需要 3.5 秒。 Python 代码类似于 Microsoft Docs 中显示的用法。但是,该事件不会在对断开连接的适配器进行的地址更改和 DNS 服务器更改时触发。

正在查看注册表

使用 win32apiRegNotifyChangeKeyValue 允许侦听注册表项 HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\Interfaces(用于地址更改)和 ...\Tcpip\Parameters\DNSRegisteredAdapters(用于连接/断开连接)子项的更改.看起来后一个条目实际上并没有存储信息本身,而是作为副作用被更改了。另一个缺点是注册表项中的更改可能与我们正在寻找的更改无关。 这种方法几乎可以立即报告地址更改(甚至比 NetworkChange 方法更快)。适配器连接也快了一点,大约 3 秒。但是,适配器断开连接要慢得多,需要 3 秒多一点才能注册。示例代码:

import win32api
import win32event
import win32con

def registry_wait():
    watchflags = win32api.REG_NOTIFY_CHANGE_NAME | win32api.REG_NOTIFY_CHANGE_LAST_SET
    if_key = win32api.RegOpenKey(win32con.HKEY_LOCAL_MACHINE, 'SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\Interfaces')
    if_evt = win32event.CreateEvent(None, 0, 0, None)
    win32api.RegNotifyChangeKeyValue(if_key, True, watchflags, if_evt, True)
    dnsada_key = win32api.RegOpenKey(win32con.HKEY_LOCAL_MACHINE, 'SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\DNSRegisteredAdapters')
    dnsada_evt = win32event.CreateEvent(None, 0, 0, None)
    win32api.RegNotifyChangeKeyValue(dnsada_key, True, watchflags, dnsada_evt, True)
    while True:
        ret_code = win32event.WaitForMultipleObjects([if_evt, dnsada_evt], False, win32event.INFINITE)
        if ret_code == win32con.WAIT_OBJECT_0 + 0:
            print('IP address info changed') # do something
            win32event.ResetEvent(if_evt)
            win32api.RegNotifyChangeKeyValue(if_key, True, watchflags, if_evt, True)
        elif ret_code == win32con.WAIT_OBJECT_0 + 1:
            print('Adapter info changed') # do something else
            win32event.ResetEvent(dnsada_evt)
            win32api.RegNotifyChangeKeyValue(dnsada_key, True, watchflags, dnsada_evt, True)
        else:
            break # set a timeout in WaitForMultipleObjects above
    win32api.RegCloseKey(dnsada_key)
    win32api.RegCloseKey(if_key)

正在查看事件日志

这就是问题中提到的方法,使用EvtSubscribe监听Microsoft-Windows-NetworkProfile/Operational事件日志。这几乎在所有方面都比其他两种方法慢。我看到地址更改需要 2 到 10 秒才能记录下来,但大多数时候大约需要 6 到 7 秒。始终连接适配器大约需要 4 秒。断开适配器的连接需要 1 秒多一点的时间,在这方面优于注册表方法。不过,NetworkChange 方法总是更快。