检测是否支持 IPv6,与操作系统无关,没有外部程序? 编辑 1:编辑 2:

问题描述

有没有一种方法可以检测系统是否支持 IPv6,而无需以与操作系统无关的方式使用外部程序

在这里和整个 Internet 上下搜索过,似乎几乎所有提供的解决方案都依赖于访问 /proc(在 Windows 和 MacOS 上不可用)或运行外部程序。

我能想到的最好的是:

import errno
import socket

def has_ip6():
    try:
        socket.create_connection(("::1",0))
    except OSError as e:
        if e.errno == errno.EADDRNOTAVAIL:
            return False
        if e.errno == errno.ECONNREFUSED;
            return True
        raise

有更好的方法吗?

解决方法

编辑:不要使用socket.has_ipv6;请查看@pepoluan 自己的答案!

socket 模块包含常量 socket.has_ipv6,专门用于指示是否支持 IPv6。

,

尽管@averresen 的答案不正确,但它确实让我找到了 https://github.com/urllib3/urllib3/pull/611#issuecomment-100954017 中指定的正确答案:

def _is_ipv6_enabled():
    """Check whether IPv6 is enabled on this host."""
    if socket.has_ipv6:
        sock = None
        try:
            sock = socket.socket(socket.AF_INET6,socket.SOCK_STREAM)
            sock.bind((HOSTv6,0))
            return True
        except OSError:
            pass
        finally:
            if sock:
                sock.close()
    return False

所以基本上我所做的已经在正确的道路上,我只需要在开始时添加一个防御性的 if not socket.has_ipv6: return False


编辑 1:

这就是我最终得到的结果:

import socket
import errno

# On Windows,the E* constants will use the WSAE* values
# So no need to hardcode an opaque integer in the sets.
_ADDR_NOT_AVAIL = {errno.EADDRNOTAVAIL,errno.EAFNOSUPPORT}
_ADDR_IN_USE = {errno.EADDRINUSE}

def system_has_ipv6() -> bool:
    if not has_ipv6:
        return False
    try:
        with socket.socket(socket.AF_INET6,socket.SOCK_STREAM) as sock:
            sock.bind(("::1",0))
        return True
    except OSError as e:
        if e.errno in _ADDR_NOT_AVAIL:
            return False
        if e.errno in _ADDR_IN_USE:
            # This point shouldn't ever be reached. But just in case...
            return True
        # Other errors should be inspected
        raise

编辑 2:

我将 errno.EAFNOSUPPORT 添加到 _ADDR_NOT_AVAIL 集。 EADDRNOTAVAIL 是 IPv6 模块/驱动程序已加载但被禁用时的 errno,EAFNOSUPPORT 是 IPv6 模块/驱动程序根本未加载时的 errno。