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?

1

32 Answers

1 2

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

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!' 

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.

12

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']).

12

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)) 
7
import subprocess ping_response = subprocess.Popen(["/bin/ping", "-c1", "-w100", "192.168.0.1"], stdout=subprocess.PIPE).stdout.read() 
8

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.

3

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.

1

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

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: 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.
2

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.

2

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 
1

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") 
3

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

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

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

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: print(isUp("192.168.1.1")) #Pings local router print(isUp("192.168.1.135")) #Pings a local computer - will differ for your network 
3

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") 
1

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!

1

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.

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 

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 

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)'.

0

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 

on linux, it's possible to create ICMP datagram (not raw) sockets without being root (or setuid or CAP_NET_RAW): . 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 ''' 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

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

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.

1

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.

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.

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

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.

1 2