在 Python 中确定计算机是否通过 WiFi 网络或以太网电缆连接到互联网

问题描述:

我想知道如何让 Python 脚本确定计算机如何连接到网络.这样做的原因是因为如果连接速度太慢,脚本就会出错,所以如果用户通过 WiFi 连接,我想显示警告.

I would like to know how to have a Python script determine how a computer is connected to a network. The reason for this is because the script becomes buggy if the connection speed is too slow, so I would like to show a warning if the user is connected via WiFi.

正在运行的实际 Python 可执行文件位于我公司的 LAN 上,因此这就是连接速度很重要的原因.此外,由于此可执行文件无法在没有连接的情况下运行,因此我不关心检查用户是否在线.

The actual Python executable being run is located on my company's LAN, so this is why the connection speed matters. Additionally, since this executable cannot be run without a connection, I am not concerned with checking that the user is online.

我对 Python 如何与此类事物进行交互一无所知(或者即使可能),所以如果有什么我可以澄清的,请告诉我.提前致谢

I know nothing about how Python interacts with things like this (or even if its possible), so please let me know if there's anything I can clarify. Thanks in advance

我在 Windows 7 64 位上使用 Python 2.7.6 32 位(不同的位,因为 pyEnchant)

I am using Python 2.7.6 32-bit on Windows 7 64-bit (different bits because pyEnchant)

在 Windows 上,此信息可通过 WMI 的 网络适配器类.

On Windows, this information is made available via WMI's Network Adapter Classes.

在 .NET 中(例如,使用 IronPython),这很容易使用:

In .NET (e.g., with IronPython), this is easy to use:

ni = Windows.Networking.Connectivity.NetworkInformation
profile = ni.GetInternetConnectionProfile()
if profile:
    interface = profile.NetworkAdapter.IanaInterfaceType
    if interface == 71:
        do WiFi stuff
    elif interface == 6:
        do Ethernet stuff
    else:
        do wtf stuff

IanaInterfaceType 是来自 此 IANA 列表.(我想解析 SNMP MIB,您可以那样下载.) 请注意,Windows 可能会将一些不太常见/过时的适配器类型与以太网混为一谈,但您可能并不关心这一点.

The IanaInterfaceType is a number from this IANA list. (I you want to parse SNMP MIBs, you can download it that way.) Note that Windows may lump some less common/obsolete adapter types in with Ethernet, but you probably don't care about that.

对于旧版本的 Windows——我不确定 Windows 7 是否算作旧版"——你想要 Win32_NetworkAdapter 和朋友,而不是 MSFT_NetworkAdapter/NetworkAdapter 作为上面有链接,所以代码会有所不同.

For older versions of Windows—I'm not sure whether Windows 7 counts as "older" or not—you want Win32_NetworkAdapter and friends, not MSFT_NetworkAdapter/NetworkAdapter as linked above, so the code will be different.

特别是,获取提供默认互联网连接的适配器更难,但更灵活,而获取其类型更容易,但信息较少.IIRC,至少在 XP 中,即使 Windows 有一些互联网连接"的概念,您无法通过 WMI 访问它——但您可以访问所有互联网连接,或搜索/迭代您关心的那个.如果您有 VPN,或者坐在 DMZ 上,或者同时有多个 NIC 卡处于活动状态等,这实际上会更好.

In particular, getting the adapter that's supplying the default internet connection is harder, but more flexible, while getting its type is easier, but less informative. IIRC, at least in XP, even though Windows had some notion of "the internet connection", you couldn't access it through WMI—but you could access all internet connections, or search/iterate for the one you care about. Which is actually better if you've got a VPN, or are sitting on a DMZ, or have multiple NIC cards active at once, etc.

所以,你要做的是搜索或迭代Win32_IP4RouteTable 以查找处理您实际想要连接的 IP 地址的连接路由,或使用 Win32_NetworkAdapaterConfiguration 以查找哪个 NIC 处理您要连接的地址.无论哪种方式,您都会获得一个 InterfaceIndex,并且您可以查找 Win32_NetworkAdapter 与该 InterfaceIndex.然后它的 AdapterType 是一个很好的字符串,你可以检查 != "Wireless".(我相信我从 TechNet 脚本中心的 VBscript 示例中弄清楚了所有这些然后我自己将它移植到 IronPython,但那是很久以前的事了,所以我可能记错了.无论如何,文档似乎比当时更完整,所以应该没有必要.)

So, what you do is to search or iterate Win32_IP4RouteTable to find which connection route handles the IP address you actually want to connect to, or use Win32_NetworkAdapaterConfiguration to find which NIC handles the address you want to connect from. Either way, you will get an InterfaceIndex, and you can look up the Win32_NetworkAdapter with that InterfaceIndex. Then its AdapterType is a nice string that you can just check != "Wireless". (I believe I figured all of this out from a VBscript example at TechNet Script Center and then ported it to IronPython myself, but it was a long time ago, so I may be remembering wrong. Anyway, the docs seem more complete than they did back then, so that shouldn't be necessary.)

如果没有 .NET(例如,使用标准 CPython),您必须使用 Web 服务接口,或者与本机 Win32 COM 接口通信.

Without .NET (e.g., with standard CPython), you have to use either the web services interface, or talk to the native Win32 COM interface.

理论上,您可以通过ctypes 直接与COM 接口对话,但这是一个可怕的想法;您几乎肯定想要 pywin32 .

In theory, you could talk to the COM interface directly via ctypes, but that's a horrible idea; you almost certainly want pywin32 for that.

而且,更好的是,我建议在 PyPI 上寻找包装器,例如这个,而不是自己构建.然后,代码应该几乎和 .NET 一样简单.

And, even better, I would suggest looking around on PyPI for wrappers, like this one, rather than building it yourself. Then, the code should be nearly as simple as with .NET.

下面是一些使用上面链接的 WMI 库和上面描述的 XP 兼容源地址方法的伪代码:

Here's some pseudcode using the WMI library linked above and the XP-compatible source-address method described above:

import wmi
MY_IP = '10.1.2.3'
w = wmi.WMI()
for nac in w.Win32_NetworkAdapterConfiguration():
    if MY_IP in nac.IPAddress:
        idx = nac.InterfaceIndex
        break
else:
    oops, do something, no NIC has that address
na = w.Win32_NetworkAdapter(InterfaceIndex=idx)
if not na:
    oops, unlikely race condition, NIC disabled right before we checked it
if na[0].AdapterType == 'Wireless':
    do WiFi stuff
elif na[0].AdapterType.startswith('Ethernet'):
    do Ethernet stuff
else:
    who's using LocalTalk in 2013?

应该可以搜索 NetworkAddressConfiguration,但我不知道如何在 wmi 库中搜索IPAddress contains MY_IP".使用 = 可能会在那里工作,谁知道;通读教程以找出答案.

It should be possible to search the NetworkAddressConfiguration, but I have no idea how to search for "IPAddress contains MY_IP" in the wmi library. Using = might work there, who knows; read through the tutorial to find out.

另一方面,按目标地址搜索应该是这样的:

Searching by target address, on the other hand, should be like this:

TARGET_IP = '10.10.10.10'
w = wmi.WMI()
rt = w.Win32_IP4RouteTable(Destination=TARGET_IP)
if not rt:
    oops, no route to host
idx = rt[0].InterfaceIndex
# same as above