¿Cómo puedo ver si hay una conexión de red activa y disponible en Python?

Resuelto aF. asked hace 14 años • 22 respuestas

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?

aF. avatar Sep 22 '10 03:09 aF.
Aceptado

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.8a 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.urlopendurante 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
unutbu avatar Sep 21 '2010 21:09 unutbu

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.8y 8.8.4.4. Podemos intentar conectarnos a cualquiera de ellos.

Un Nmap rápido del host 8.8.8.8arrojó 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/tcpestá abierto y no filtrado. Si no es un usuario root, recuerde utilizar sudoo el -Pnargumento 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.8comunicarnos 53/tcp. Ahora podemos configurar una conexión de socket en 8.8.8.8:53/tcpPython 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

Trueen 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 3sde forma predeterminada.

7h3rAm avatar Oct 14 '2015 05:10 7h3rAm