Pinging servers in Python
Asked Answered
N

33

271

In Python, is there a way to ping a server through ICMP and return TRUE if the server responds, or FALSE if there is no response?

Nerine answered 1/6, 2010 at 21:27 Comment(1)
related: Ping a site in Python?Filefish
S
207

This function works in any OS (Unix, Linux, macOS, and Windows)
Python 2 and Python 3

EDITS:
By @radato os.system was replaced by subprocess.call. This avoids shell injection vulnerability in cases where your hostname string might not be validated.

import platform    # For getting the operating system name
import subprocess  # For executing 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.
    """

    # Option for the number of packets as a function of
    param = '-n' if platform.system().lower()=='windows' else '-c'

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

    return subprocess.call(command) == 0

Note that, according to @ikrase on Windows this function will still return True if you get a Destination Host Unreachable error.

Explanation

The command is ping in both Windows and Unix-like systems.
The option -n (Windows) or -c (Unix) controls the number of packets which in this example was set to 1.

platform.system() returns the platform name. Ex. 'Darwin' on macOS.
subprocess.call() performs a system call. Ex. subprocess.call(['ls','-l']).

Stillas answered 20/9, 2015 at 22:24 Comment(13)
Note that this will still return true (on Windows) if you get a "destination host unreachable" reply from a different host.Mislay
I'm finding that I will occasionally get a ping success when my modem is off??? That's testing "8.8.8.8" and "google.com" on a Windows 10 OS. Something is not quite right.Ozonize
That cannot happen @Markus. Please, test by hand and with a modified version of the code above, and tell us the result. By hand: 1) open cmd 2) ping 8.8.8.8 -n 1 3) echo %ERRORLEVEL%. Code: Modify the last line of the Python code to return system_call(command). With proper connectivity you will get 0 (zero). With your modem off, you must get some error code. Of course, both methods must return the same error code under same conditions.Stillas
It did happen and I was using the exact code, word for word. I understand and belief your comments, there is no way a command line ping could succeed when there in no connection, hence my thought something wasn't working correctly in the python to command line operation. I'll give the update a try and see how it goes. Thanks.Ozonize
@Ozonize see ikrase's comment.Sugden
Am I the only one here to notice that system.call is a typo and should be subprocess.call?Boatswain
You are right. Some one edited my original answer. I will fix that. ThanksStillas
How would we get the response time as a variable?Beckerman
How would you make a cross-compatible timeout flag?Neese
I can confirm that the windows ping command is hokey with its return value. I am pinging a system I have disconnected from the network, another IP is responding that it isn't available, but I am getting 0% loss and an ERRORLEVEL of 0. Here is a paste of the results pastebin.pl/view/2437bb7cTalc
If this doesn't work well on windows, it would be good to remove it from the title. This is not the super-universal solution.Same
Hello @Sany. This solution woks on Windows. The problem reported by ikrase could be solved. I can image two ways of doing that. However, I do not have any installation of Windows available to try. Maybe somebody in a future could improve the answer by including that fix.Stillas
In a Celery task and Python 3 it returns FileNotFoundError: [Errno 2] No such file or directory: 'ping': 'ping'Nobility
D
224

If you don't need to support Windows, here's a really concise way to do it:

import os
param = '-n' if os.sys.platform().lower()=='win32' else '-c'
hostname = "google.com" #example
response = os.system(f"ping {param} 1 {hostname}")

#and then check the response...
if response == 0:
  print(f"{hostname} is up!")
else:
  print(f"{hostname} is down!")

This works because ping returns a non-zero value if the connection fails. (The return value actually differs depending on the network error.) You could also change the ping timeout (in seconds) using the '-t' option. Note, this will output text to the console.

Dropsy answered 1/5, 2012 at 18:29 Comment(14)
I ended up with this variant response = os.system("ping -c 1 -w2 " + hostname + " > /dev/null 2>&1")Mm
@jeckyll2hide man ping, send just 1 packet with deadline 2 seconds and redirect all output to /dev/null, retrieve just the return value.Mm
@ManuelGutierrez I think you want "-W 2000" (timeout after 2000 milliseconds) and maybe "-t 3" (exit after 3 seconds, no matter what)Cathe
You missed to quote hostname!Sardine
-w and -W take values in seconds not milliseconds. Check man ping to make sure.Paulus
I'm just curious - why won't this actually work on Windows?Diuresis
@SourceMatters The param to specify number of packets is different for windows (-n) and unix (-c).Judijudicable
Ahh ok cool! I ultimately ended up doing the 2nd Answer instead, and parse the output, as I was a victim of the "Destination Host Unreachable" responses as well.Diuresis
If you get the hostname string from a user, they could easily hack your server by giving you a "url" like 'google.com; rm -rf /*'. Use subprocess.run(["ping", "-c", "1", hostname]).returncode instead.Sugden
How would we get the ping response time?Beckerman
Here's my solution from Python 3.6, using the newer subprocess.run: command = ["ping", "-c", "1", "-w2", host] return subprocess.run(args=command, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL).returncode == 0Stabilizer
On Windows, ping -w argument takes MILISECONDS.Belgium
I tried this but it gives me always 1 as result. I think this need to be run in admin mode but any idea on how to do it?Argosy
In case someone didn't mention this before os.sys.platform is not a method, it's a string. >>> os.sys.platform() Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: 'str' object is not callable The appropriate call is: >>> os.sys.platform.lower() 'linux'Unpleasant
S
207

This function works in any OS (Unix, Linux, macOS, and Windows)
Python 2 and Python 3

EDITS:
By @radato os.system was replaced by subprocess.call. This avoids shell injection vulnerability in cases where your hostname string might not be validated.

import platform    # For getting the operating system name
import subprocess  # For executing 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.
    """

    # Option for the number of packets as a function of
    param = '-n' if platform.system().lower()=='windows' else '-c'

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

    return subprocess.call(command) == 0

Note that, according to @ikrase on Windows this function will still return True if you get a Destination Host Unreachable error.

Explanation

The command is ping in both Windows and Unix-like systems.
The option -n (Windows) or -c (Unix) controls the number of packets which in this example was set to 1.

platform.system() returns the platform name. Ex. 'Darwin' on macOS.
subprocess.call() performs a system call. Ex. subprocess.call(['ls','-l']).

Stillas answered 20/9, 2015 at 22:24 Comment(13)
Note that this will still return true (on Windows) if you get a "destination host unreachable" reply from a different host.Mislay
I'm finding that I will occasionally get a ping success when my modem is off??? That's testing "8.8.8.8" and "google.com" on a Windows 10 OS. Something is not quite right.Ozonize
That cannot happen @Markus. Please, test by hand and with a modified version of the code above, and tell us the result. By hand: 1) open cmd 2) ping 8.8.8.8 -n 1 3) echo %ERRORLEVEL%. Code: Modify the last line of the Python code to return system_call(command). With proper connectivity you will get 0 (zero). With your modem off, you must get some error code. Of course, both methods must return the same error code under same conditions.Stillas
It did happen and I was using the exact code, word for word. I understand and belief your comments, there is no way a command line ping could succeed when there in no connection, hence my thought something wasn't working correctly in the python to command line operation. I'll give the update a try and see how it goes. Thanks.Ozonize
@Ozonize see ikrase's comment.Sugden
Am I the only one here to notice that system.call is a typo and should be subprocess.call?Boatswain
You are right. Some one edited my original answer. I will fix that. ThanksStillas
How would we get the response time as a variable?Beckerman
How would you make a cross-compatible timeout flag?Neese
I can confirm that the windows ping command is hokey with its return value. I am pinging a system I have disconnected from the network, another IP is responding that it isn't available, but I am getting 0% loss and an ERRORLEVEL of 0. Here is a paste of the results pastebin.pl/view/2437bb7cTalc
If this doesn't work well on windows, it would be good to remove it from the title. This is not the super-universal solution.Same
Hello @Sany. This solution woks on Windows. The problem reported by ikrase could be solved. I can image two ways of doing that. However, I do not have any installation of Windows available to try. Maybe somebody in a future could improve the answer by including that fix.Stillas
In a Celery task and Python 3 it returns FileNotFoundError: [Errno 2] No such file or directory: 'ping': 'ping'Nobility
U
58

There is a module called pyping that can do this. It can be installed with pip

pip install pyping

It is pretty simple to use, however, when using this module, you need root access due to the fact that it is crafting raw packets under the hood.

import pyping

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

if r.ret_code == 0:
    print("Success")
else:
    print("Failed with {}".format(r.ret_code))
Underprivileged answered 8/10, 2015 at 14:26 Comment(7)
"Note that ICMP messages can only be sent from processes running as root (in Windows, you must run this script as ‘Administrator’)."Diacid
I like that you can specify the timeout and count of ICMP requests sent. I was able to write a script which discovers all hosts on the local sub-net. It executes in 1 second, instead of 255 seconds using the os.system('ping -c 1 -t 1 hostname') solution. Plus the pyping lib is very easy to use compared to using the TCP/IP sockets library. I wrote my ping program using both, and pyping is far quicker and easier to use, in my opinion, especially if one isn't familiar with using the TCP/IP sockets library.Geldens
not work with py3. ModuleNotFoundError: No module named 'core'Salmonella
the 'core' error comes from an incompatibility with python3. i tried to fix it for python3 but it constantly keeps sending me errors. the authors and projects github page is down (404 not found). we'll have to port it by ourselves to python3 :-)Systematize
for python3 try ping3: github.com/kyan001/ping3 pip install ping3Disharoon
@Salmonella - I have encountered it as well. Maybe this will help: #35331464Earnest
ping3: res = ping('8.8.8.8') Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/home/env-admin/.local/lib/python3.6/site-packages/ping3.py", line 165, in ping with socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_ICMP) as sock: File "/usr/lib/python3.6/socket.py", line 144, in init _socket.socket.__init__(self, family, type, proto, fileno) PermissionError: [Errno 1] Operation not permittedLinkboy
H
43

For python3 there's a very simple and convenient python module ping3: (pip install ping3, needs root privileges).

from ping3 import ping, verbose_ping
ping('example.com')  # Returns delay in seconds.
>>> 0.215697261510079666

This module allows for the customization of some parameters as well.

Hartung answered 21/12, 2017 at 13:50 Comment(4)
As edited requires root privileges, discussion on lifting this here: github.com/kyan001/ping3/issues/10Ambages
Oh, need root prvilege not only for install, but also for execution: ping("example.com")Altamira
This doesn't need sudo for execution. I'm running python 3.8.10Worn
ping3 now transparently/automagically switches to UDP if ICMP doesn't work. so root privileges are no longer required. Note that a "UDP ping" is, technically, not the same as a real ICMP ping, but it does the job in most use cases.Armillas
T
36
import subprocess
ping_response = subprocess.Popen(["/bin/ping", "-c1", "-w100", "192.168.0.1"], stdout=subprocess.PIPE).stdout.read()
Trustful answered 1/6, 2010 at 21:34 Comment(9)
The only problem with this is that it wouldn't work on Windows.Nerine
It should be mentioned that the reason something like this is necessary is that ICMP requires root, and /bin/ping gets around this by being set SUID.Syreetasyria
Note: May fail if ping is in a different location. Use whereis ping to get the correct path.Kamerad
This works on Windows: ping_response = subprocess.Popen(["ping", hostname, "-n", '1'], stdout=subprocess.PIPE).stdout.read()Unforgettable
How can I parse the result to check if the response was ok or ko in Windows?Parcenary
To create the subprocess arguments, I usually do "ping -c1 -w100 192.168.0.1".split()Kindling
If you want only 0 or 1, as a response : ping_response = subprocess.call(["ping", "-c1", "-w100", "10.0.0.254"], stdout=subprocess.DEVNULL)Biblioclast
The above solution does not work in Windows, even when adapted : subprocess.call(["ping", "-n", "1", "10.0.0.24"]) It always returns 0.Biblioclast
Why -w100 parameter is used?Haldes
T
36

using socket package in 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
Tinctorial answered 22/4, 2021 at 16:47 Comment(1)
Not ICMP but great way to test connection when you can't rely on underlying OS calls. And compact.Comely
C
28

Programmatic ICMP ping is complicated due to the elevated privileges required to send raw ICMP packets, and calling ping binary is ugly. For server monitoring, you can achieve the same result using a technique called 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

Internally, this simply establishes a TCP connection to the target server and drops it immediately, measuring time elapsed. This particular implementation is a bit limited in that it doesn't handle closed ports but for your own servers it works pretty well.

Chaddie answered 15/2, 2018 at 11:28 Comment(3)
Such strategy is also useful when the server has a firewall that drops real ICMP pings! Also, here's the GitHub page: github.com/zhengxiaowai/tcpingUnbonnet
How to grab the return? Looks like it prints to stdout.Flexor
An alternative package is TCPPingLibRotation
K
20

Because I like to have my Python program universal on version 2.7 and 3.x and on platform Linux, Mac OS and Windows, I had to modify the existing examples.

# 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"))
Kreda answered 25/2, 2016 at 10:47 Comment(6)
Instead of False if platform.system().lower()=="windows" else True you could of course also just use platform.system().lower() != "windows".Practical
Doesn't os.name!="nt" also work? Admittedly I've not tried it on all ver/platform combos!Sleeve
In my case the default gateway returns an 'unreachable' message, but the windows ping command still has a return code of 0. So this approach worked (sorry for the formatting - its 6 lines, including the functiontion declaration): def ping(host): process = subprocess.Popen(["ping", "-n", "1",host], stdout=subprocess.PIPE, stderr=subprocess.PIPE) streamdata = process.communicate()[0] if 'unreachable' in str(streamdata): return 1 return process.returncodeExtraditable
@wellspokenman you would rather return 0 if unreachable is found in the pipe, no?Equal
@Equal yeah I did that too but forgot to update the comment. My current function looks like this: pastebin.com/FEYWsVjKExtraditable
Thanks @wellspokenman, pretty much nothing here worked for me correctly under Windows until I tried your solution posted on pastebin.com. Thank you. It's also very fast.Lacewing
E
14

My version of a ping function:

  • Works on Python 3.5 and later, on Windows and Linux.
  • On Windows, returns False if the ping command fails with "Destination Host Unreachable".
  • And does not show any output, either as a pop-up window or in command line.
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
    '''
    # 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

Feel free to use it as you will.

Erythropoiesis answered 12/4, 2019 at 16:39 Comment(2)
Timeout is measured in seconds, not milliseconds, so a default value of 1000 is unlikely to be useful. Please edit to just "1".Wreak
@Wreak Timeout parameter must be passed in milliseconds, in Windows and Linux. I have just looked for the Mac OS command, and it uses seconds; but anyway, I can't test it on a Mac.Erythropoiesis
T
13

After looking around, I ended up writing my own ping module, which is designed to monitor large numbers of addresses, is asynchronous and doesn't use a lot of system resources. You can find it here: https://github.com/romana/multi-ping/ It's Apache licensed, so you can use it in your project in any way you see fit.

The main reasons for implementing my own are the restrictions of the other approaches:

  • Many of the solutions mentioned here require an exec out to a command line utility. This is quite inefficient and resource hungry if you need to monitor large numbers of IP addresses.
  • Others mention some older python ping modules. I looked at those and in the end, they all had some issue or the other (such as not correctly setting packet IDs) and didn't handle the ping-ing of large numbers of addresses.
Thud answered 17/8, 2017 at 22:47 Comment(2)
Nice job mate! If anyone wants to see it in action, just use github.com/romana/multi-ping/blob/master/demo.pyDrabble
It's worth mentioning that this too requires root privileges.Coming
E
12
#!/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()
Epencephalon answered 1/10, 2014 at 16:25 Comment(1)
This code may have the answer to the question, but it would be helpful to add some comments or explanation of how your code is solving the problem.Cornu
D
9

If your server does not support ICMP (firewall might block it), it most probably still offers a service on a TCP port. In this case, you can perform a TCP ping1 (platform independently and without installing additional python modules) like this:

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()

The code is taken and only slightly modified from here.


1 A TCP ping does not really exist as a ping is performed with ICMP on ISO/OSI layer 3. A TCP ping is performed on ISO/OSI layer 4. It just tries to connect to a TCP port in the most basic way, that it is not transmitting any data, but closing the connection immediately after connecting.

Decree answered 5/5, 2022 at 7:34 Comment(0)
A
8

I resolve this with:

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" is the way to know if the ping is correctly. Saludos

Arnoldarnoldo answered 6/9, 2017 at 15:30 Comment(1)
No problem wit Destination Host Unreachable error - see comment above from @Mislay ?Same
T
6

Make Sure pyping is installed or install it pip install pyping

#!/usr/bin/python
import pyping

response = pyping.ping('Your IP')

if response.ret_code == 0:
    print("reachable")
else:
    print("unreachable")
Therefor answered 16/12, 2015 at 6:17 Comment(3)
Thanks! However, I need to run this code as root to make it work.Wonderment
Pyping's GitHub page no longer exists and the PyPI package has not been updated since 2016.Beckerman
I got thefollowing errors: import pyping Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/usr/local/lib/python3.6/dist-packages/pyping/__init__.py", line 3, in <module> from core import * ModuleNotFoundError: No module named 'core'Altamira
E
5
#!/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 !")
Epencephalon answered 1/10, 2014 at 15:7 Comment(0)
T
5

My reduction using ideas from answers in this post but only using the newer recommended subprocess module and python3:

import subprocess
import platform

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

def ping(ip):
    # ping_command = ['ping', ip, '-n', '1'] instead of ping_command = ['ping', ip, '-n 1'] for Windows
    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)
Temporal answered 20/8, 2016 at 23:42 Comment(1)
You don't need to use True if condition else False for returning True or False based on a condition. Just use e.g. shell_needed = operating_sys == 'Windows' and return success == 0Applecart
U
5

on linux, it's possible to create ICMP datagram (not raw) sockets without being root (or setuid or CAP_NET_RAW): https://unix.stackexchange.com/a/592914. I ended up with

$ 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

though many of the packages other answers have suggested here would work too

Undulate answered 14/8, 2021 at 3:54 Comment(0)
P
4

This script works on Windows, and should work on other OSes : It works on Windows, Debian, and macosx, need a test on 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
Pillbox answered 13/2, 2015 at 22:0 Comment(3)
Good answer. No admin rights required for Windows here.Levanter
Am either way receiving True both a right and wrong IPAtheling
Yeah this definitely doesn't work. Just returns :"true" either way on WindowsLacewing
C
3

Using Multi-ping (pip install multiPing) I made this simple code (simply copy and paste if you will!):

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

Usage:

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

If you want a single sample, the second parameter "10" can be ignored!

Hope it helps!

Church answered 2/12, 2017 at 14:3 Comment(1)
awesome library, but need root privileges.Spoofery
F
2

Seems simple enough, but gave me fits. I kept getting "icmp open socket operation not permitted" or else the solutions would hang up if the server was off line. If, however, what you want to know is that the server is alive and you are running a web server on that server, then curl will do the job. If you have ssh and certificates, then ssh and a simple command will suffice. Here is the code:

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
Fizz answered 10/2, 2015 at 20:18 Comment(0)
H
2

Use this it's tested on python 2.7 and works fine it returns ping time in milliseconds if success and return False on fail.

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
Hydrocellulose answered 11/4, 2015 at 17:7 Comment(0)
J
2

I had similar requirement so i implemented it as shown below. It is tested on Windows 64 bit and 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))

When IP is not reachable subprocess.check_output() raises an exception. Extra verification can be done by extracting information from output line 'Packets: Sent = 2, Received = 2, Lost = 0 (0% loss)'.

Jarrett answered 21/5, 2015 at 10:58 Comment(0)
D
2

I ended up finding this question regarding a similar scenario. I tried out pyping but the example given by Naveen didn't work for me in Windows under Python 2.7.

An example that worked for me is:

import pyping

response = pyping.send('Your IP')

if response['ret_code'] == 0:
    print("reachable")
else:
    print("unreachable")
Doubles answered 28/12, 2015 at 17:58 Comment(1)
pyping does not appear to be a standard module. Perhaps you could provide a link?Teredo
L
2

Here's a solution using Python's subprocess module and the ping CLI tool provided by the underlying OS. Tested on Windows and Linux. Support setting a network timeout. Doesn't need root privileges (at least on Windows and 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
Larianna answered 13/2, 2018 at 12:45 Comment(0)
W
2

WINDOWS ONLY - Can't believe no-ones cracked open Win32_PingStatus Using a simple WMI query we return an object full of really detailed info for free

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'

sample output

Weeden answered 8/6, 2018 at 13:24 Comment(1)
This works great, simple and clean plus the return object has all kinds of useful information.Orozco
D
2

My take borrowing from other answers. Attempt to simplify and minimize queries.

import platform, os

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

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

EDIT: ignoring case in return as per comment by Olivier B.

Demolition answered 26/10, 2018 at 5:46 Comment(1)
This seem to works great, but ttl is lowercase on OSX on the ping command output, so for it to works, you need return 'TTL=' in result or 'ttl=' in resultTonus
H
2

I needed a faster ping sweep and I didn't want to use any external libraries, so I resolved to using concurrency using built-in asyncio.

This code requires python 3.7+ and is made and tested on Linux only. It won't work on Windows but I am sure you can easily change it to work on Windows.

I ain't an expert with asyncio but I used this great article Speed Up Your Python Program With Concurrency and I came up with these lines of codes. I tried to make it as simple as possible, so most likely you will need to add more code to it to suit your needs.

It doesn't return true or false, I thought it would be more convenient just to make it print the IP that responds to a ping request. I think it is pretty fast, pinging 255 ips in nearly 10 seconds.

#!/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())

Sample output:

192.168.1.1
192.168.1.3
192.168.1.102
192.168.1.106
192.168.1.6

Note that the IPs are not in order, as the IP is printed as soon it replies, so the one that responds first gets printed first.

Hawkbill answered 13/3, 2020 at 13:37 Comment(0)
S
2
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
Speedy answered 20/3, 2022 at 7:53 Comment(0)
P
2

The only half-way satisfying solution I came up with is this:

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

Works under Windows and Linux using unprivileged accounts via simple sockets.

Apparently also supports async/multi "ping".

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

Polyphonic answered 3/9, 2023 at 7:7 Comment(0)
A
1

Ping them all in windows or linux, return a sorted list. This is a hybrid/fix from the responses @Ahmed Essam and @Arno.

import asyncio
import re

import platform
isWindows = platform.system()


async def ping(host):
    cmd = 'ping {} {} 1'.format(host, '-n' if isWindows else '-c')
    ping_proc = \
        await asyncio.create_subprocess_shell(cmd, stdout=asyncio.subprocess.PIPE,
                                                      stderr=asyncio.subprocess.PIPE)
    stdout, stderr = await ping_proc.communicate()
    outstr = stdout.decode()

    if ping_proc.returncode == 0:
        delay = int(re.search(r'(?:time=)([\d]*)', outstr).group(1)) if 'time=' in outstr else -1
        if delay >= 0:
            # print('{} {}ms'.format(host, delay))
            return [host, delay]

    return [host, None]


async def ping_all():
    tasks = []

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

    retList = await asyncio.gather(*tasks, return_exceptions=True)
    retList = [x for x in retList if x[1] is not None]
    retList.sort(key=lambda x: int(x[0].split('.')[-1]))

    return retList


loop = asyncio.ProactorEventLoop()
asyncio.set_event_loop(loop)
pingRet = loop.run_until_complete(ping_all())

for ip, d in pingRet:
    print('{:<16s} {}ms'.format(ip, d))
Andesine answered 28/5, 2021 at 22:17 Comment(0)
T
1

My own method that combines several answers above:


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)

Tested on OSX Monterey.

Tonus answered 14/3, 2022 at 14:53 Comment(0)
F
0

One thing a lot of the answers miss is that (at least in Windows) the ping command returns 0 (indicating success) if it receives the reply "Destination host unreachable."

Here is my code that checks if b'TTL=' is in the response, since that is only present when the ping reached the host. Note: Most of this code is based on the other answers here.

import platform
import subprocess

def ping(ipAddr, timeout=100):
    '''
    Send a ping packet to the specified host, using the system ping command.
    Accepts ipAddr as string for the ping destination.
    Accepts timeout in ms for the ping timeout.
    Returns True if ping succeeds otherwise Returns False.
        Ping succeeds if it returns 0 and the output includes b'TTL='
    '''
    if platform.system().lower() == 'windows':
        numFlag = '-n'
    else:
        numFlag = '-c'
    completedPing = subprocess.run(['ping', numFlag, '1', '-w', str(timeout), ipAddr],
                                   stdout=subprocess.PIPE,    # Capture standard out
                                   stderr=subprocess.STDOUT)  # Capture standard error
    # print(completedPing.stdout)
    return (completedPing.returncode == 0) and (b'TTL=' in completedPing.stdout)

print(ping('google.com'))

Note: This captures the output instead of printing it, so if you want to see the output of ping, you'll need to print completedPing.stdout before returning.

Fingered answered 2/3, 2018 at 16:48 Comment(1)
This doesn't work on OSX. -w does not exist -> -W ; and it returns False when the ping call actually pings.Tonus
K
-3
  1 #!/usr/bin/python
  2
  3 import os
  4 import sys
  5 import time
  6
  7 os.system("clear")
  8 home_network = "172.16.23."
  9 mine = []
 10
 11 for i in range(1, 256):
 12         z =  home_network + str(i)
 13         result = os.system("ping -c 1 "+ str(z))
 14         os.system("clear")
 15         if result == 0:
 16                 mine.append(z)
 17
 18 for j in mine:
 19         print "host ", j ," is up"

A simple one i just cooked up in a minute..using icmplib needs root privs the below works pretty good! HTH

Kutenai answered 15/3, 2014 at 12:37 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.