如何在Python中查看是否有可用且活动的网络连接?

177 投票
22 回答
274214 浏览
提问于 2025-04-16 04:26

我想看看能不能访问一个在线的API,但为了做到这一点,我需要有网络连接。

我该怎么用Python检查一下是否有可用的网络连接呢?

22 个回答

105

直接发一个HEAD请求会更快,因为这样就不会下载任何HTML内容。

try:
    import httplib  # python < 3.0
except:
    import http.client as httplib


def have_internet() -> bool:
    conn = httplib.HTTPSConnection("8.8.8.8", timeout=5)
    try:
        conn.request("HEAD", "/")
        return True
    except Exception:
        return False
    finally:
        conn.close()
156

如果我们能连接到某个互联网服务器,那就说明我们有网络连接。不过,为了确保连接速度快且可靠,所有解决方案至少应该满足以下几点要求:

  • 避免使用DNS解析(我们需要一个大家都知道的IP地址,并且这个地址大部分时间都能用)
  • 避免应用层连接(比如连接HTTP/FTP/IMAP服务)
  • 避免从Python或其他语言调用外部工具(我们需要一个不依赖第三方解决方案的通用方法)

为了满足这些要求,我们可以尝试检查一下是否能连接到Google的公共DNS服务器。这些服务器的IPv4地址是8.8.8.88.8.4.4。我们可以尝试连接其中任何一个。

对主机8.8.8.8进行快速的Nmap扫描,结果如下:

$ sudo nmap 8.8.8.8

Starting Nmap 6.40 ( http://nmap.org ) at 2015-10-14 10:17 IST
Nmap scan report for google-public-dns-a.google.com (8.8.8.8)
Host is up (0.0048s latency).
Not shown: 999 filtered ports
PORT   STATE SERVICE
53/tcp open  domain

Nmap done: 1 IP address (1 host up) scanned in 23.81 seconds

从结果来看,53/tcp端口是开放的,没有被过滤。如果你不是管理员用户,记得使用sudo或者Nmap的-Pn参数,这样可以发送特制的探测包,确认主机是否在线。

在用Python测试之前,我们先用一个外部工具Netcat来检查连接:

$ nc 8.8.8.8 53 -zv
Connection to 8.8.8.8 53 port [tcp/domain] succeeded!

Netcat确认我们可以通过53/tcp访问8.8.8.8。现在我们可以在Python中设置一个socket连接到8.8.8.8:53/tcp来检查连接:

import socket

def internet(host="8.8.8.8", port=53, timeout=3):
    """
    Host: 8.8.8.8 (google-public-dns-a.google.com)
    OpenPort: 53/tcp
    Service: domain (DNS/TCP)
    """
    try:
        socket.setdefaulttimeout(timeout)
        socket.socket(socket.AF_INET, socket.SOCK_STREAM).connect((host, port))
        return True
    except socket.error as ex:
        print(ex)
        return False

internet()

另一种方法是手动发送一个DNS探测包到这些服务器,并等待响应。不过,我猜这样可能会比较慢,因为可能会有数据包丢失、DNS解析失败等问题。如果你有不同的看法,请留言告诉我。

更新 #4: 这个公共名称服务器的列表是测试IP的一个好参考。

更新 #3: 在处理异常的更改后再次测试:

defos.py
True
00:00:00:00.410

iamaziz.py
True
00:00:00:00.240

ivelin.py
True
00:00:00:00.109

jaredb.py
True
00:00:00:00.520

kevinc.py
True
00:00:00:00.317

unutbu.py
True
00:00:00:00.436

7h3rAm.py
True
00:00:00:00.030

更新 #2: 我进行了快速测试,以找出所有有效答案中最快和最通用的实现。以下是总结:

$ ls *.py | sort -n | xargs -I % sh -c 'echo %; ./timeit.sh %; echo'
defos.py
True
00:00:00:00.487

iamaziz.py
True
00:00:00:00.335

ivelin.py
True
00:00:00:00.105

jaredb.py
True
00:00:00:00.533

kevinc.py
True
00:00:00:00.295

unutbu.py
True
00:00:00:00.546

7h3rAm.py
True
00:00:00:00.032

再来一次:

$ ls *.py | sort -n | xargs -I % sh -c 'echo %; ./timeit.sh %; echo'
defos.py
True
00:00:00:00.450

iamaziz.py
True
00:00:00:00.358

ivelin.py
True
00:00:00:00.099

jaredb.py
True
00:00:00:00.585

kevinc.py
True
00:00:00:00.492

unutbu.py
True
00:00:00:00.485

7h3rAm.py
True
00:00:00:00.035

上面输出中的True表示这些作者的实现都能正确识别出与互联网的连接。时间以毫秒为单位显示。

更新 #1: 感谢@theamk的评论,现在超时设置为一个参数,默认初始化为3s

157

也许你可以试试这样的代码:

from urllib import request

def internet_on():
    try:
        request.urlopen('http://216.58.192.142', timeout=1)
        return True
    except request.URLError as err: 
        return False

如果你用的是Python 2.x,可以把导入的那行改成 import urllib2 as request

现在,8.8.8.8是谷歌的一个IP地址。你可以把 http://8.8.8.8 改成任何一个能快速响应的网站

这个固定的IP地址不会永远指向google.com。所以这段代码不够稳定——你需要不断维护它,才能让它正常工作。

上面的代码使用固定IP地址而不是完整的域名(FQDN),是因为使用FQDN需要进行DNS查询。当机器没有正常的网络连接时,DNS查询可能会让 urllib_request.urlopen 的调用阻塞超过一秒。感谢@rzetterberg指出了这一点。


如果上面的固定IP地址不工作,你可以通过在unix系统上运行以下命令来找到google.com的当前IP地址:

% dig google.com  +trace 
...
google.com.     300 IN  A   216.58.192.142

撰写回答