Пинг серверы в Python

Есть ли в Python способ пропинговать сервер через ICMP и возвращать TRUE, если сервер отвечает, или FALSE, если ответа нет?

34 ответа

Если вам не нужна поддержка Windows, вот очень краткий способ сделать это:

import os
hostname = "google.com" #example
response = os.system("ping -c 1 " + hostname)

#and then check the response...
if response == 0:
  print hostname, 'is up!'
else:
  print hostname, 'is down!'

Это работает, потому что ping возвращает ненулевое значение в случае сбоя соединения. (Возвращаемое значение на самом деле отличается в зависимости от сетевой ошибки.) Вы также можете изменить время ожидания пинга (в секундах), используя опцию '-t'. Обратите внимание, это выведет текст на консоль.

Эта функция работает в любой ОС (Unix, Linux, macOS и Windows)
Python 2 и Python 3

РЕДАКТИРОВАТЬ 1: Добавлены некоторые комментарии к оригинальному ответу.
РЕДАКТИРОВАТЬ 2: После предложения radato, os.system был заменен subprocess.call,

from platform   import system as system_name  # Returns the system/OS name
from subprocess import call   as system_call  # Execute a shell command

def ping(host):
    """
    Returns True if host (str) responds to a ping request.
    Remember that a host may not respond to a ping (ICMP) request even if the host name is valid.
    """

    # Ping command count option as function of OS
    param = '-n' if system_name().lower()=='windows' else '-c'

    # Building the command. Ex: "ping -c 1 google.com"
    command = ['ping', param, '1', host]

    # Pinging
    return system_call(command) == 0

Команда ping как в Windows, так и в Unix-подобных системах. Опция -n (Windows) или -c (Unix) контролирует количество пакетов, которое в этом примере равно 1.

platform.system() возвращает имя платформы Ex. 'Darwin' в macOS
subprocess.call() выполняет системный вызов. Ex. subprocess.call(['ls','-l'])

Существует модуль, называемый pyping, который может сделать это. Может быть установлен с пипс

pip install pyping

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

import pyping

r = pyping.ping('google.com')

if r.ret_code == 0:
    print("Success")
else:
    print("Failed with {}".format(r.ret_code))

Мне понравился ping3 https://github.com/kyan001/ping3 Очень просто и удобно!

from ping3 import ping, verbose_ping
ping('example.com')  # Returns delay in seconds.
>>>0.215697261510079666
import subprocess
ping_response = subprocess.Popen(["/bin/ping", "-c1", "-w100", "192.168.0.1"], stdout=subprocess.PIPE).stdout.read()

используя пакет сокетов в python3:

      import socket

def ping_server(server: str, port: int, timeout=3):
    """ping server"""
    try:
        socket.setdefaulttimeout(timeout)
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        s.connect((server, port))
    except OSError as error:
        return False
    else:
        s.close()
        return True

Программный пинг ICMP сложен из-за повышенных привилегий, необходимых для отправки необработанных пакетов ICMP и вызова ping бинарный уродлив. Для мониторинга сервера вы можете достичь того же результата, используя метод TCP ping:

# pip3 install tcping
>>> from tcping import Ping
# Ping(host, port, timeout)
>>> ping = Ping('212.69.63.54', 22, 60)
>>> ping.ping(3)
Connected to 212.69.63.54[:22]: seq=1 time=23.71 ms
Connected to 212.69.63.54[:22]: seq=2 time=24.38 ms
Connected to 212.69.63.54[:22]: seq=3 time=24.00 ms

Внутренне это просто устанавливает TCP-соединение с целевым сервером и немедленно сбрасывает его, измеряя прошедшее время. Эта конкретная реализация немного ограничена в том, что она не обрабатывает закрытые порты, но для ваших собственных серверов она работает довольно хорошо.

Поскольку мне нравится, что моя программа Python универсальна для версий 2.7 и 3.x и для платформ Linux, Mac OS и Windows, мне пришлось изменить существующие примеры.

# shebang does not work over all platforms
# ping.py  2016-02-25 Rudolf
# subprocess.call() is preferred to os.system()
# works under Python 2.7 and 3.4
# works under Linux, Mac OS, Windows

def ping(host):
    """
    Returns True if host responds to a ping request
    """
    import subprocess, platform

    # Ping parameters as function of OS
    ping_str = "-n 1" if  platform.system().lower()=="windows" else "-c 1"
    args = "ping " + " " + ping_str + " " + host
    need_sh = False if  platform.system().lower()=="windows" else True

    # Ping
    return subprocess.call(args, shell=need_sh) == 0

# test call
print(ping("192.168.17.142"))

Моя версия функции ping:

  • Работает на Python 3.5 и более поздних версиях, в Windows и Linux (должен работать на Mac, но не может его протестировать).
  • В Windows возвращает значение False, если команда ping завершается с ошибкой "Целевой хост недоступен".
  • И не показывает никакого вывода, ни в виде всплывающего окна, ни в командной строке.
import platform, subprocess

def ping(host_or_ip, packets=1, timeout=1000):
    ''' Calls system "ping" command, returns True if ping succeeds.
    Required parameter: host_or_ip (str, address of host to ping)
    Optional parameters: packets (int, number of retries), timeout (int, ms to wait for response)
    Does not show any output, either as popup window or in command line.
    Python 3.5+, Windows and Linux compatible (Mac not tested, should work)
    '''
    # The ping command is the same for Windows and Linux, except for the "number of packets" flag.
    if platform.system().lower() == 'windows':
        command = ['ping', '-n', str(packets), '-w', str(timeout), host_or_ip]
        # run parameters: capture output, discard error messages, do not show window
        result = subprocess.run(command, stdin=subprocess.DEVNULL, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL, creationflags=0x08000000)
        # 0x0800000 is a windows-only Popen flag to specify that a new process will not create a window.
        # On Python 3.7+, you can use a subprocess constant:
        #   result = subprocess.run(command, capture_output=True, creationflags=subprocess.CREATE_NO_WINDOW)
        # On windows 7+, ping returns 0 (ok) when host is not reachable; to be sure host is responding,
        # we search the text "TTL=" on the command output. If it's there, the ping really had a response.
        return result.returncode == 0 and b'TTL=' in result.stdout
    else:
        command = ['ping', '-c', str(packets), '-w', str(timeout), host_or_ip]
        # run parameters: discard output and error messages
        result = subprocess.run(command, stdin=subprocess.DEVNULL, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
        return result.returncode == 0

Не стесняйтесь использовать его, как вы будете.

Посмотрев вокруг, я написал свой собственный модуль ping, который предназначен для мониторинга большого количества адресов, является асинхронным и не использует много системных ресурсов. Вы можете найти его здесь: https://github.com/romana/multi-ping/ Он лицензирован Apache, поэтому вы можете использовать его в своем проекте так, как считаете нужным.

Основными причинами реализации моих собственных являются ограничения других подходов:

  • Многие из упомянутых здесь решений требуют exec для утилиты командной строки. Это довольно неэффективно и требует много ресурсов, если вам нужно отслеживать большое количество IP-адресов.
  • Другие упоминают некоторые старые модули ping для Python. Я посмотрел на них, и, в конце концов, у них у всех была та или иная проблема (например, неправильная установка идентификаторов пакетов), и они не обрабатывали пинг большого количества адресов.
#!/usr/bin/python3

import subprocess as sp

def ipcheck():
    status,result = sp.getstatusoutput("ping -c1 -w2 " + str(pop))
    if status == 0:
        print("System " + str(pop) + " is UP !")
    else:
        print("System " + str(pop) + " is DOWN !")


pop = input("Enter the ip address: ")
ipcheck()

Я решаю это с помощью:

def ping(self, host):
    res = False

    ping_param = "-n 1" if system_name().lower() == "windows" else "-c 1"

    resultado = os.popen("ping " + ping_param + " " + host).read()

    if "TTL=" in resultado:
        res = True
    return res

"TTL" - это способ узнать, правильно ли выполнен пинг. Saludos

Если ваш сервер не поддерживает ICMP (брандмауэр может его заблокировать), он, скорее всего, все еще предлагает услугу на TCP-порту. В этом случае вы можете выполнить TCP ping1 (независимо от платформы и без установки дополнительных модулей Python) следующим образом:

      import socket

def isReachable(ipOrName, port, timeout=2):
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.settimeout(timeout)
    try:
        s.connect((ipOrName, int(port)))
        s.shutdown(socket.SHUT_RDWR)
        return True
    except:
        return False
    finally:
        s.close()

Код взят и лишь немного изменен отсюда .


1 Проверка связи TCP на самом деле не существует, так как проверка связи выполняется с помощью ICMP на уровне 3 ISO/OSI. Проверка связи TCP выполняется на уровне 4 ISO/OSI. он не передает никаких данных, а закрывает соединение сразу после соединения.

Убедитесь, что установлен pyping или установите его pip install pyping

#!/usr/bin/python
import pyping

response = pyping.ping('Your IP')

if response.ret_code == 0:
    print("reachable")
else:
    print("unreachable")

Мое сокращение - использование идей из ответов в этом посте, но только с использованием более нового рекомендуемого модуля подпроцесса и python3:

import subprocess
import platform

operating_sys = platform.system()
nas = '192.168.0.10'

def ping(ip):

    ping_command = ['ping', ip, '-n 1'] if operating_sys == 'Windows' else ['ping', ip, '-c 1']
    shell_needed = True if operating_sys == 'Windows' else False

    ping_output = subprocess.run(ping_command,shell=shell_needed,stdout=subprocess.PIPE)
    success = ping_output.returncode
    return True if success == 0 else False

out = ping(nas)
print(out)
#!/usr/bin/python3

import subprocess as sp

ip = "192.168.122.60"
status,result = sp.getstatusoutput("ping -c1 -w2 " + ip)

if status == 0: 
    print("System " + ip + " is UP !")
else:
    print("System " + ip + " is DOWN !")

Этот скрипт работает в Windows и должен работать в других ОС: он работает в Windows, Debian и macosx, требуется тестирование на Solaris.

import os
import platform


def isUp(hostname):

    giveFeedback = False

    if platform.system() == "Windows":
        response = os.system("ping "+hostname+" -n 1")
    else:
        response = os.system("ping -c 1 " + hostname)

    isUpBool = False
    if response == 0:
        if giveFeedback:
            print hostname, 'is up!'
        isUpBool = True
    else:
        if giveFeedback:
            print hostname, 'is down!'

    return isUpBool

print(isUp("example.com")) #Example domain
print(isUp("localhost")) #Your computer
print(isUp("invalid.example.com")) #Unresolvable hostname: https://tools.ietf.org/html/rfc6761
print(isUp("192.168.1.1")) #Pings local router
print(isUp("192.168.1.135")) #Pings a local computer - will differ for your network

в Linux можно создавать сокеты для датаграмм ICMP (не сырые), не будучи root (или setuid, или CAP_NET_RAW): /questions/3634099/naskolko-ploh-etot-kod/3634107#3634107. Я закончил с

      $ id
uid=1000(raylu) gid=1000(raylu) [...]
$ sudo sysctl net.ipv4.ping_group_range='1000 1000'
      import socket
import struct
import time

def main():
    ping('192.168.1.10')

def ping(destination):
    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.getprotobyname('icmp'))
    sock.settimeout(10.0)
    start_time = time.time_ns() # python 3.7+ only

    payload = struct.pack('L', start_time)
    sock.sendto(encode(payload), (destination, 0))
    while (time.time_ns() - start_time) // 1_000_000_000 < 10:
        try:
            data, source = sock.recvfrom(256)
        except socket.timeout:
            print('timed out')
            return
        message_type, message_code, check, identifier, sequence_number = struct.unpack('bbHHh', data[:8])
        if source == (destination, 0) and message_type == ICMP.ECHO_REPLY and data[8:] == payload:
            print((time.time_ns() - start_time) // 1_000_000, 'ms')
            break
        else:
            print('got unexpected packet from %s:' % source[0], message_type, data[8:])
    else:
        print('timed out')

def encode(payload: bytes):
    # calculate checksum with check set to 0
    checksum = calc_checksum(icmp_header(ICMP.ECHO_REQUEST, 0, 0, 1, 1) + payload)
    # craft the packet again with the checksum set
    return icmp_header(ICMP.ECHO_REQUEST, 0, checksum, 1, 1) + payload

def icmp_header(message_type, message_code, check, identifier, sequence_number) -> bytes:
    return struct.pack('bbHHh', message_type, message_code, check, identifier, sequence_number)

def calc_checksum(data: bytes) -> int:
    '''RFC 1071'''
    # code stolen from https://github.com/alessandromaggio/pythonping/blob/a59ce65a/pythonping/icmp.py#L8
    '''
    MIT License

    Copyright (c) 2018 Alessandro Maggio

    Permission is hereby granted, free of charge, to any person obtaining a copy
    of this software and associated documentation files (the "Software"), to deal
    in the Software without restriction, including without limitation the rights
    to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    copies of the Software, and to permit persons to whom the Software is
    furnished to do so, subject to the following conditions:

    The above copyright notice and this permission notice shall be included in all
    copies or substantial portions of the Software.

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    SOFTWARE.
    '''
    subtotal = 0
    for i in range(0, len(data)-1, 2):
        subtotal += (data[i] << 8) + data[i+1]
    if len(data) % 2:
        subtotal += (data[len(data)-1] << 8)
    while subtotal >> 16:
        subtotal = (subtotal & 0xFFFF) + (subtotal >> 16)
    check = ~subtotal
    return ((check << 8) & 0xFF00) | ((check >> 8) & 0x00FF)

class ICMP:
    ECHO_REPLY = 0
    ECHO_REQUEST = 8

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

Использование Multi-PING (pip install multiPing) Я сделал этот простой код (просто скопируйте и вставьте, если хотите!):

from multiping import MultiPing

def ping(host,n = 0):
    if(n>0):
        avg = 0
        for i in range (n):
            avg += ping(host)
        avg = avg/n
    # Create a MultiPing object to test hosts / addresses
    mp = MultiPing([host])

    # Send the pings to those addresses
    mp.send()

    # With a 1 second timout, wait for responses (may return sooner if all
    # results are received).
    responses, no_responses = mp.receive(1)


    for addr, rtt in responses.items():
        RTT = rtt


    if no_responses:
        # Sending pings once more, but just to those addresses that have not
        # responded, yet.
        mp.send()
        responses, no_responses = mp.receive(1)
        RTT = -1

    return RTT

Использование:

#Getting the latency average (in seconds) of host '192.168.0.123' using 10 samples
ping('192.168.0.123',10)

Если вы хотите один образец, второй параметр "10"можно игнорировать!

Надеюсь, поможет!

Я закончил тем, что нашел этот вопрос относительно похожего сценария. Я попробовал pyping, но пример, данный Naveen, не работал для меня в Windows под Python 2.7.

Пример, который работал для меня:

import pyping

response = pyping.send('Your IP')

if response['ret_code'] == 0:
    print("reachable")
else:
    print("unreachable")

Мне нужна была более быстрая проверка связи, и я не хотел использовать какие-либо внешние библиотеки, поэтому решил использовать параллелизм с использованием встроенных asyncio.

Для этого кода требуется Python 3.7+, он создан и протестирован только на Linux. Он не будет работать в Windows, но я уверен, что вы можете легко изменить его для работы в Windows.

Я не эксперт в asyncioно я использовал эту замечательную статью " Ускорьте вашу программу Python с помощью параллелизма" и придумал эти строки кода. Я постарался сделать его как можно более простым, поэтому, скорее всего, вам нужно будет добавить в него больше кода в соответствии с вашими потребностями.

Он не возвращает true или false, я подумал, что было бы удобнее просто заставить его печатать IP, который отвечает на запрос ping. Я думаю, что это довольно быстро, пингуется 255 ips почти за 10 секунд.

#!/usr/bin/python3

import asyncio

async def ping(host):
    """
    Prints the hosts that respond to ping request
    """
    ping_process = await asyncio.create_subprocess_shell("ping -c 1 " + host + " > /dev/null 2>&1")
    await ping_process.wait()

    if ping_process.returncode == 0:
        print(host)
    return 


async def ping_all():
    tasks = []

    for i in range(1,255):
        ip = "192.168.1.{}".format(i)
        task = asyncio.ensure_future(ping(ip))
        tasks.append(task)

    await asyncio.gather(*tasks, return_exceptions = True)

asyncio.run(ping_all())

Пример вывода:

192.168.1.1
192.168.1.3
192.168.1.102
192.168.1.106
192.168.1.6

Обратите внимание, что IP-адреса не в порядке, поскольку IP-адрес печатается, как только он отвечает, поэтому тот, который отвечает первым, печатается первым.

Кажется достаточно простым, но дал мне припадки. Я продолжал получать сообщение "Работа с открытым сокетом ICMP запрещена", иначе решения зависали бы, если бы сервер был отключен. Если, однако, вы хотите знать, что сервер активен и на этом сервере запущен веб-сервер, то curl сделает эту работу. Если у вас есть ssh и сертификаты, то достаточно ssh и простой команды. Вот код:

from easyprocess import EasyProcess # as root: pip install EasyProcess
def ping(ip):
    ping="ssh %s date;exit"%(ip) # test ssh alive or
    ping="curl -IL %s"%(ip)      # test if http alive
    response=len(EasyProcess(ping).call(timeout=2).stdout)
    return response #integer 0 if no response in 2 seconds

У меня было подобное требование, поэтому я реализовал его, как показано ниже. Он протестирован на 64-битной Windows и Linux.

import subprocess
def systemCommand(Command):
    Output = ""
    Error = ""     
    try:
        Output = subprocess.check_output(Command,stderr = subprocess.STDOUT,shell='True')
    except subprocess.CalledProcessError as e:
        #Invalid command raises this exception
        Error =  e.output 

    if Output:
        Stdout = Output.split("\n")
    else:
        Stdout = []
    if Error:
        Stderr = Error.split("\n")
    else:
        Stderr = []

    return (Stdout,Stderr)

#in main
Host = "ip to ping"
NoOfPackets = 2
Timeout = 5000 #in milliseconds
#Command for windows
Command = 'ping -n {0} -w {1} {2}'.format(NoOfPackets,Timeout,Host)
#Command for linux 
#Command = 'ping -c {0} -w {1} {2}'.format(NoOfPackets,Timeout,Host)
Stdout,Stderr = systemCommand(Command)
if Stdout:
   print("Host [{}] is reachable.".format(Host))
else:
   print("Host [{}] is unreachable.".format(Host))

Когда IP недоступен, subprocess.check_output() вызывает исключение. Дополнительную проверку можно выполнить путем извлечения информации из строки вывода "Пакеты: отправлено = 2, получено = 2, потеряно = 0 (потеря 0%)".

Используйте это, он протестирован на python 2.7 и работает нормально, он возвращает время пинга в миллисекундах, если успех, и возвращает False при ошибке.

import platform,subproccess,re
def Ping(hostname,timeout):
    if platform.system() == "Windows":
        command="ping "+hostname+" -n 1 -w "+str(timeout*1000)
    else:
        command="ping -i "+str(timeout)+" -c 1 " + hostname
    proccess = subprocess.Popen(command, stdout=subprocess.PIPE)
    matches=re.match('.*time=([0-9]+)ms.*', proccess.stdout.read(),re.DOTALL)
    if matches:
        return matches.group(1)
    else: 
        return False
      import os #to get clear screen
import subprocess as sp #to get system ping
os.system("clear") #clear screen
print('Wait or Press Ctrl+Z to Terminate\n') #notice to terminate or wait
for i in range(255): #0 to 255 loop
    ip='192.168.1.'+str(i) #concatenating str and int
    s,r=sp.getstatusoutput("ping -c1 -w2 " + ip) #ping and store status in s
    if s==0: #if status is 0 equal to pass
        print(ip+" is UP ✓ ") #output
    else: #if status is not 0 equal to fail
        pass #skip and try next ip from loop

Единственное удовлетворительное решение, которое я придумал на полпути, это следующее:

      from tcppinglib import tcpping
tcpping('example.com', 443, 1, 1).is_alive

Работает под Windows и Linux с использованием непривилегированных учетных записей через простые сокеты.

Судя по всему, также поддерживается асинхронный/множественный «пинг».

https://pypi.org/project/tcppinglib/

Вот решение с использованием Python subprocess модуль и ping Инструмент CLI, предоставляемый базовой ОС. Проверено на Windows и Linux. Поддержка установки таймаута сети. Не нужны права root (по крайней мере, в Windows и Linux).

import platform
import subprocess

def ping(host, network_timeout=3):
    """Send a ping packet to the specified host, using the system "ping" command."""
    args = [
        'ping'
    ]

    platform_os = platform.system().lower()

    if platform_os == 'windows':
        args.extend(['-n', '1'])
        args.extend(['-w', str(network_timeout * 1000)])
    elif platform_os in ('linux', 'darwin'):
        args.extend(['-c', '1'])
        args.extend(['-W', str(network_timeout)])
    else:
        raise NotImplemented('Unsupported OS: {}'.format(platform_os))

    args.append(host)

    try:
        if platform_os == 'windows':
            output = subprocess.run(args, check=True, universal_newlines=True).stdout

            if output and 'TTL' not in output:
                return False
        else:
            subprocess.run(args, check=True)

        return True
    except (subprocess.CalledProcessError, subprocess.TimeoutExpired):
        return False

Я беру заимствования из других ответов. Попытка упростить и минимизировать запросы.

import platform, os

def ping(host):
    result = os.popen(' '.join(("ping", ping.param, host))).read()
    return 'TTL=' in result

ping.param = "-n 1" if platform.system().lower() == "windows" else "-c 1"

ТОЛЬКО для WINDOWS - Не могу поверить, что никто не взломал Win32_PingStatus Используя простой WMI-запрос, мы возвращаем объект, полный действительно подробной информации, бесплатно

import wmi


# new WMI object
c = wmi.WMI()

# here is where the ping actually is triggered
x = c.Win32_PingStatus(Address='google.com')

# how big is this thing? - 1 element
print 'length x: ' ,len(x)


#lets look at the object 'WMI Object:\n'
print x


#print out the whole returned object
# only x[0] element has values in it
print '\nPrint Whole Object - can directly reference the field names:\n'
for i in x:
    print i



#just a single field in the object - Method 1
print 'Method 1 ( i is actually x[0] ) :'
for i in x:
    print 'Response:\t', i.ResponseTime, 'ms'
    print 'TTL:\t', i.TimeToLive


#or better yet directly access the field you want
print '\npinged ', x[0].ProtocolAddress, ' and got reply in ', x[0].ResponseTime, 'ms'

образец вывода

Мой собственный метод, который сочетает в себе несколько ответов выше:

      
def ping(host, show_log=False, package_count=1):
    ping.param = "-n" if platform.system().lower() == 'windows' else "-c"
    result = subprocess.run(['ping', ping.param, str(package_count), host],
                            stdout=subprocess.PIPE,
                            stderr=subprocess.STDOUT)
    output = result.stdout
    if show_log:
        print('return code: ', result.returncode)
        print(output.decode("utf-8"))
    return result.returncode == 0 and (b'TTL=' in output or b'ttl=' in output)

Протестировано на OSX Monterey.

Другие вопросы по тегам