¿Cómo puedo ver si hay una conexión de red activa y disponible en Python?
Quiero ver si puedo acceder a una API en línea, pero para eso necesito tener acceso a Internet.
¿Cómo puedo ver si hay una conexión disponible y activa usando Python?
Quizás podrías usar algo como esto:
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
Para Python 2.x, reemplace la declaración de importación por import urllib2 as request
:
Actualmente, 8.8.8.8 es una de las direcciones IP de Google. Cambie http://8.8.8.8
a cualquier sitio del que se pueda esperar que responda rápidamente .
Esta IP fija no se asignará a google.com para siempre. Por lo tanto, este código no es sólido: necesitará un mantenimiento constante para que siga funcionando.
La razón por la que el código anterior utiliza una dirección IP fija en lugar de un nombre de dominio completo (FQDN) es porque un FQDN requeriría una búsqueda de DNS. Cuando la máquina no tiene una conexión a Internet que funcione, la búsqueda de DNS puede bloquear la llamada urllib_request.urlopen
durante más de un segundo. Gracias a @rzetterberg por señalar esto.
Si la dirección IP fija anterior no funciona, puede encontrar una dirección IP actual para google.com (en Unix) ejecutando
% dig google.com +trace
...
google.com. 300 IN A 216.58.192.142
Si podemos conectarnos a algún servidor de Internet, entonces efectivamente tenemos conectividad. Sin embargo, para lograr el enfoque más rápido y confiable, todas las soluciones deben cumplir con los siguientes requisitos, como mínimo:
- Evitar la resolución DNS (necesitaremos una IP que sea conocida y que garantice su disponibilidad la mayor parte del tiempo)
- Evite conexiones de capa de aplicación (conectarse a un servicio HTTP/FTP/IMAP)
- Evite llamadas a utilidades externas desde Python u otro lenguaje de elección (necesitamos crear una solución independiente del lenguaje que no dependa de soluciones de terceros)
Para cumplir con estos, un enfoque podría ser verificar si uno de los servidores DNS públicos de Google es accesible. Las direcciones IPv4 de estos servidores son 8.8.8.8
y 8.8.4.4
. Podemos intentar conectarnos a cualquiera de ellos.
Un Nmap rápido del host 8.8.8.8
arrojó el siguiente resultado:
$ 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
Como podemos ver, 53/tcp
está abierto y no filtrado. Si no es un usuario root, recuerde utilizar sudo
o el -Pn
argumento para que Nmap envíe paquetes de sonda diseñados y determine si un host está activo.
Antes de probar con Python, probemos la conectividad usando una herramienta externa, Netcat:
$ nc 8.8.8.8 53 -zv
Connection to 8.8.8.8 53 port [tcp/domain] succeeded!
Netcat confirma que podemos 8.8.8.8
comunicarnos 53/tcp
. Ahora podemos configurar una conexión de socket en 8.8.8.8:53/tcp
Python para verificar la conexión:
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()
Otro enfoque podría ser enviar una sonda DNS diseñada manualmente a uno de estos servidores y esperar una respuesta. Pero supongo que podría resultar más lento en comparación debido a caídas de paquetes, fallas en la resolución de DNS, etc. Por favor comente si piensa lo contrario.
ACTUALIZACIÓN #4: Esta lista de servidores de nombres públicos es una buena referencia para realizar pruebas con las IP.
ACTUALIZACIÓN #3: Probado nuevamente después del cambio en el manejo de excepciones:
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
ACTUALIZACIÓN #2: Hice pruebas rápidas para identificar la implementación más rápida y genérica de todas las respuestas válidas a esta pregunta. Aquí está el resumen:
$ 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
Y una vez más:
$ 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
en el resultado anterior significa que todas estas implementaciones de los respectivos autores identifican correctamente la conectividad a Internet. El tiempo se muestra con una resolución de milisegundos.
ACTUALIZACIÓN #1: Gracias al comentario de @theamk, el tiempo de espera ahora es un argumento y se inicializa 3s
de forma predeterminada.