在Python中,是否有一种方法可以通过ICMP来ping服务器,如果服务器响应则返回TRUE,如果没有响应则返回FALSE ?


当前回答

在windows或linux中Ping它们,返回一个排序的列表。这是@Ahmed Essam和@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))

其他回答

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

使用这个,它在python 2.7上测试过,工作很好,如果成功,它返回以毫秒为单位的ping时间,失败时返回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

在linux上,可以创建ICMP数据报(不是原始)套接字,而不需要root(或setuid或CAP_NET_RAW): https://unix.stackexchange.com/a/592914。最后我得到了

$ 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

虽然这里其他答案建议的许多软件包也可以工作

这个脚本适用于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

我的版本的ping函数:

适用于Python 3.5及更高版本,适用于Windows和Linux。 在Windows上,如果ping命令失败且“目标主机不可达”,则返回False。 并且不显示任何输出,无论是弹出窗口还是命令行。

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

你想怎么用就怎么用。