如何在Python中查看是否有可用且活动的网络连接?
我想看看能不能访问一个在线的API,但为了做到这一点,我需要有网络连接。
我该怎么用Python检查一下是否有可用的网络连接呢?
22 个回答
直接发一个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()
如果我们能连接到某个互联网服务器,那就说明我们有网络连接。不过,为了确保连接速度快且可靠,所有解决方案至少应该满足以下几点要求:
- 避免使用DNS解析(我们需要一个大家都知道的IP地址,并且这个地址大部分时间都能用)
- 避免应用层连接(比如连接HTTP/FTP/IMAP服务)
- 避免从Python或其他语言调用外部工具(我们需要一个不依赖第三方解决方案的通用方法)
为了满足这些要求,我们可以尝试检查一下是否能连接到Google的公共DNS服务器。这些服务器的IPv4地址是8.8.8.8
和8.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
。
也许你可以试试这样的代码:
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