Проверка сетевого подключения

Я хочу посмотреть, смогу ли я получить доступ к онлайн-API, но для этого мне нужен доступ в Интернет.

Как узнать, доступно ли соединение и используется ли оно с помощью Python?

22 ответа

Решение

Возможно, вы могли бы использовать что-то вроде этого:

import urllib2

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

В настоящее время 216.58.192.142 является одним из IP-адресов google.com. + Изменить http://216.58.192.142 на любой сайт можно ожидать быстрого ответа.

Этот фиксированный IP-адрес не будет навсегда привязан к google.com. Таким образом, этот код не является надежным - для его работы потребуется постоянное обслуживание.

Причина, по которой в приведенном выше коде используется фиксированный IP-адрес вместо полного доменного имени (FQDN), заключается в том, что для полного доменного имени требуется поиск DNS. Если на компьютере нет работающего подключения к Интернету, сам поиск DNS может заблокировать вызов urllib_request.urlopen более секунды. Спасибо @rzetterberg за то, что указал на это.


Если указанный выше фиксированный IP-адрес не работает, вы можете найти текущий IP-адрес для google.com (в Unix), запустив

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

Если мы можем подключиться к какому-либо интернет-серверу, то у нас действительно есть возможность подключения. Однако для наиболее быстрого и надежного подхода все решения должны как минимум соответствовать следующим требованиям:

  • Избегайте разрешения DNS (нам понадобится общеизвестный IP-адрес, который гарантированно будет доступен в большинстве случаев)
  • Избегайте соединений на уровне приложений (подключение к службе HTTP/FTP/IMAP)
  • Избегайте вызовов внешних утилит из Python или другого языка по вашему выбору (нам нужно придумать решение, не зависящее от языка, которое не зависит от сторонних решений)

Чтобы соответствовать им, одним из подходов может быть проверка доступности одного из общедоступных DNS-серверов Google. Адреса IPv4 для этих серверов 8.8.8.8 а также 8.8.4.4, Мы можем попробовать подключиться к любому из них.

Быстрый Nmap хоста 8.8.8.8 дал ниже результат:

$ 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

Как мы видим, TCP/53 открыт и не фильтруется. Если вы не являетесь пользователем root, не забудьте использовать sudo или -Pn аргумент для Nmap для отправки специально сформированных тестовых пакетов и определения, работает ли хост.

Прежде чем мы попробуем использовать Python, давайте проверим подключение с помощью внешнего инструмента Netcat:

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

Netcat подтверждает, что мы можем достичь 8.8.8.8 по TCP/53. Теперь мы можем установить сокетное соединение на 8.8.8.8:53/TCP в Python для проверки соединения:

>>> 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 Exception as ex:
...     print ex.message
...     return False
...
>>> internet()
True
>>>

Другим подходом может быть отправка вручную созданного DNS-запроса на один из этих серверов и ожидание ответа. Но, я полагаю, это может оказаться медленнее по сравнению из-за отбрасывания пакетов, сбоя разрешения DNS и т. Д. Пожалуйста, прокомментируйте, если считаете иначе

ОБНОВЛЕНИЕ № 1: Благодаря комментарию @theamk, тайм-аут теперь является аргументом и по умолчанию инициализируется 3 с.

ОБНОВЛЕНИЕ № 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 в приведенном выше выводе означает, что все эти реализации от соответствующих авторов правильно идентифицируют подключение к Интернету. Время показывается с разрешением в миллисекундах.

ОБНОВЛЕНИЕ № 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

Будет быстрее сделать запрос HEAD, поэтому HTML не будет загружен.
Также я уверен, что Google хотел бы, чтобы это лучше:)

try:
    import httplib
except:
    import http.client as httplib

def have_internet():
    conn = httplib.HTTPConnection("www.google.com", timeout=5)
    try:
        conn.request("HEAD", "/")
        conn.close()
        return True
    except:
        conn.close()
        return False

В качестве альтернативы ответам Убутну / Кевина С я использую requests пакет, как это:

import requests

def connected_to_internet(url='http://www.google.com/', timeout=5):
    try:
        _ = requests.get(url, timeout=timeout)
        return True
    except requests.ConnectionError:
        print("No internet connection available.")
    return False

Бонус: это может быть расширено до этой функции, которая пингует веб-сайт.

def web_site_online(url='http://www.google.com/', timeout=5):
    try:
        req = requests.get(url, timeout=timeout)
        # HTTP errors are not raised by default, this statement does that
        req.raise_for_status()
        return True
    except requests.HTTPError as e:
        print("Checking internet connection failed, status code {0}.".format(
        e.response.status_code))
    except requests.ConnectionError:
        print("No internet connection available.")
    return False

Просто чтобы обновить сказанное unutbu для нового кода в Python 3.2

def check_connectivity(reference):
    try:
        urllib.request.urlopen(reference, timeout=1)
        return True
    except urllib.request.URLError:
        return False

И, просто чтобы заметить, входные данные (ссылка) - это URL, который вы хотите проверить: я предлагаю выбрать что-то, что быстро соединяется с вашим местом проживания - то есть я живу в Южной Корее, поэтому я бы, вероятно, установил ссылку на http://www.naver.com/.

Вы можете просто попытаться загрузить данные, и в случае сбоя соединения вы будете знать, что что-то с соединением не в порядке.

По сути, вы не можете проверить, подключен ли компьютер к Интернету. Причин может быть много, например, неправильная конфигурация DNS, брандмауэры, NAT. Поэтому, даже если вы сделаете несколько тестов, вы не сможете гарантировать, что у вас будет соединение с API, пока вы не попробуете.

import urllib

def connected(host='http://google.com'):
    try:
        urllib.urlopen(host)
        return True
    except:
        return False

# test
print( 'connected' if connected() else 'no internet!' )

Для Python 3 используйте urllib.request.urlopen(host)

Попробуйте операцию, которую вы пытались сделать в любом случае. Если это не удается, Python должен выдать вам исключение, чтобы вы знали.

Попытка сначала выполнить простую операцию для обнаружения соединения будет представлять собой состояние гонки. Что делать, если интернет-соединение действует, когда вы тестируете, но обрывается, прежде чем вам нужно будет выполнить реальную работу?

Вот моя версия

import requests

try:
    if requests.get('https://google.com').ok:
        print("You're Online")
except:
    print("You're Offline")

Это может не работать, если localhost был изменен с 127.0.0.1Пытаться

import socket
ipaddress=socket.gethostbyname(socket.gethostname())
if ipaddress=="127.0.0.1":
    print("You are not connected to the internet!")
else:
    print("You are connected to the internet with the IP address of "+ ipaddress )

Если не отредактировано, IP вашего компьютера будет 127.0.0.1, если он не подключен к Интернету. Этот код в основном получает IP-адрес, а затем спрашивает, является ли он IP-адресом локального хоста. надеюсь, это поможет

Современное портативное решение с requests:

import requests

def internet():
    """Detect an internet connection."""

    connection = None
    try:
        r = requests.get("https://google.com")
        r.raise_for_status()
        print("Internet connection detected.")
        connection = True
    except:
        print("Internet connection not detected.")
        connection = False
    finally:
        return connection

Или версия, вызывающая исключение:

import requests
from requests.exceptions import ConnectionError

def internet():
    """Detect an internet connection."""

    try:
        r = requests.get("https://google.com")
        r.raise_for_status()
        print("Internet connection detected.")
    except ConnectionError as e:
        print("Internet connection not detected.")
        raise e

Мой любимый, когда запускаются скрипты на кластере или нет

import subprocess

def online(timeout):
    try:
        return subprocess.run(
            ['wget', '-q', '--spider', 'google.com'],
            timeout=timeout
        ).returncode == 0
    except subprocess.TimeoutExpired:
        return False

это работает тихо, не загружая ничего, но проверяя, что данный удаленный файл существует в сети

Лучший способ сделать это - проверить IP-адрес, который всегда дает python, если он не может найти веб-сайт. В этом случае это мой код:

import socket

print("website connection checker")
while True:
    website = input("please input website: ")
    print("")
    print(socket.gethostbyname(website))
    if socket.gethostbyname(website) == "92.242.140.2":
        print("Website could be experiencing an issue/Doesn't exist")
    else:
        socket.gethostbyname(website)
        print("Website is operational!")
        print("")

Принимая ответ Ивелина и добавьте дополнительную проверку, поскольку мой маршрутизатор выдает свой IP-адрес 192.168.0.1 и возвращает запрос, если у него нет подключения к Интернету при запросе google.com.

def haveInternet():
    try:
        # first check if we get the correct IP-Address or just the router's IP-Address
        info = socket.getaddrinfo("www.google.com", None)[0]
        ipAddr = info[4][0]
        if ipAddr == "192.168.0.1" :
            return False
    except:
        return False

    conn = httplib.HTTPConnection("www.google.com", timeout=5)
    try:
        conn.request("HEAD", "/")
        conn.close()
        return True
    except:
        conn.close()
        return False

В своих проектах я использую скрипт, модифицированный для проверки связи с публичным DNS-сервером Google 8.8.8.8. Используя тайм-аут в 1 секунду и основные библиотеки Python без внешних зависимостей:

import struct
import socket
import select


def send_one_ping(to='8.8.8.8'):
   ping_socket = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.getprotobyname('icmp'))
   checksum = 49410
   header = struct.pack('!BBHHH', 8, 0, checksum, 0x123, 1)
   data = b'BCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwx'
   header = struct.pack(
      '!BBHHH', 8, 0, checksum, 0x123, 1
   )
   packet = header + data
   ping_socket.sendto(packet, (to, 1))
   inputready, _, _ = select.select([ping_socket], [], [], 1.0)
   if inputready == []:
      raise Exception('No internet') ## or return False
   _, address = ping_socket.recvfrom(2048)
   print(address) ## or return True


send_one_ping()

Значение тайм-аута выбора равно 1, но может быть числом с плавающей запятой, чтобы отказать быстрее, чем 1 секунда в этом примере.

Запросы на импорт и попробуйте этот простой код Python.

def check_internet():
url = 'http://www.google.com/'
timeout = 5
try:
    _ = requests.get(url, timeout=timeout)
    return True
except requests.ConnectionError:
return False

Убедитесь, что ваш пункт обновлен, запустив

pip install --upgrade pip

Установите пакет запросов, используя

pip install requests  
    import requests
    import webbrowser
    url = "http://www.youtube.com"

    timeout = 6
    try:
        request = requests.get(url, timeout=timeout)
        print("Connected to the Internet")
        print("browser is loading url")
        webbrowser.open(url)
    except (requests.ConnectionError, requests.Timeout) as exception:
       print("poor or no internet connection.")

Это работает для меня в Python3.6

import urllib
from urllib.request import urlopen


def is_internet():
    """
    Query internet using python
    :return:
    """
    try:
        urlopen('https://www.google.com', timeout=1)
        return True
    except urllib.error.URLError as Error:
        print(Error)
        return False


if is_internet():
    print("Internet is active")
else:
    print("Internet disconnected")

Принимая ответ Six, я думаю, что мы могли бы как-то упростить, важный вопрос, так как новички теряются в технических вопросах.

Вот что я, наконец, буду использовать, чтобы дождаться, пока мое соединение (3G, медленное) будет устанавливаться один раз в день для моего PV мониторинга.

Работает под Pyth3 с Raspbian 3.4.2

from urllib.request import urlopen
from time import sleep
urltotest=http://www.lsdx.eu             # my own web page
nboftrials=0
answer='NO'
while answer=='NO' and nboftrials<10:
    try:
        urlopen(urltotest)
        answer='YES'
    except:
        essai='NO'
        nboftrials+=1
        sleep(30)       

Максимальный пробег: 5 минут, если достигнут, я постараюсь через час, но это еще один сценарий!

Взяв ответ unutbu за отправную точку и сгоревший в прошлом из-за "статического" изменения IP-адреса, я создал простой класс, который проверяет один раз с помощью поиска DNS (т. Е. С использованием URL-адреса) https://www.google.com/"), а затем сохраняет IP-адрес отвечающего сервера для использования при последующих проверках. Таким образом, IP-адрес всегда актуален (при условии, что класс повторно инициализируется не реже одного раза в несколько лет). Я также благодарю Гаври за этот ответ, который показал мне, как получить IP-адрес сервера (после любого перенаправления и т. Д.). Пожалуйста, не обращайте внимания на очевидное хакерство этого решения, я приведу здесь минимальный рабочий пример.:)

Вот что у меня есть:

import socket

try:
    from urllib2 import urlopen, URLError
    from urlparse import urlparse
except ImportError:  # Python 3
    from urllib.parse import urlparse
    from urllib.request import urlopen, URLError

class InternetChecker(object):
    conn_url = 'https://www.google.com/'

    def __init__(self):
        pass

    def test_internet(self):
        try:
            data = urlopen(self.conn_url, timeout=5)
        except URLError:
            return False

        try:
            host = data.fp._sock.fp._sock.getpeername()
        except AttributeError:  # Python 3
            host = data.fp.raw._sock.getpeername()

        # Ensure conn_url is an IPv4 address otherwise future queries will fail
        self.conn_url = 'http://' + (host[0] if len(host) == 2 else
                                     socket.gethostbyname(urlparse(data.geturl()).hostname))

        return True

# Usage example
checker = InternetChecker()
checker.test_internet()

Я добавил несколько в код Джоэла.

    import socket,time
    mem1 = 0
    while True:
        try:
                host = socket.gethostbyname("www.google.com") #Change to personal choice of site
                s = socket.create_connection((host, 80), 2)
                s.close()
                mem2 = 1
                if (mem2 == mem1):
                    pass #Add commands to be executed on every check
                else:
                    mem1 = mem2
                    print ("Internet is working") #Will be executed on state change

        except Exception as e:
                mem2 = 0
                if (mem2 == mem1):
                    pass
                else:
                    mem1 = mem2
                    print ("Internet is down")
        time.sleep(10) #timeInterval for checking

Я просто хочу сослаться на решение Ивелина, потому что я не могу комментировать его.

В python 2.7 со старым SSL-сертификатом (в моем случае невозможно обновить, что является другой историей) существует вероятность ошибки сертификата. В этом случае может помочь замена «8.8.8.8» на «dns.google» или «8888.google».

Надеюсь, это тоже кому-то поможет.

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


def have_internet():
    conn = httplib.HTTPSConnection("8888.google", timeout=5)
    try:
        conn.request("HEAD", "/")
        return True
    except Exception:
        return False
    finally:
        conn.close()
Другие вопросы по тегам