在Python中对服务器进行ping测试

265
在Python中,是否有一种通过ICMP对服务器进行ping并在服务器响应时返回TRUE,或者在无响应时返回FALSE的方法?

33个回答

216

如果您不需要支持Windows,这是一种非常简洁的方法:

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

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

这是因为如果连接失败,ping 将返回一个非零值。(实际上,返回值会根据网络错误而有所不同。)您还可以使用“-t”选项更改 ping 超时时间(以秒为单位)。请注意,这将在控制台输出文本。

54
我最终得到了这个变量 response = os.system("ping -c 1 -w2 " + hostname + " > /dev/null 2>&1") - MGP
4
使用命令"man ping",发送一个带有2秒截止时间的数据包并将所有输出重定向到/dev/null,只检索返回值。 - MGP
1
-w 和 -W 的值应该以秒为单位而不是毫秒。请查看 man ping 确认。 - Alan Turing
18
如果您从用户那里获取 hostname 字符串,他们可能会通过提供像 'google.com; rm -rf /*' 这样的 "url" 轻松地攻击您的服务器。请改用 subprocess.run(["ping", "-c", "1", hostname]).returncode - user3064538
4
这是我使用较新的 subprocess.run 在 Python 3.6 中的解决方案:command = ["ping", "-c", "1", "-w2", host]return subprocess.run(args=command, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL).returncode == 0 - Ian Colwell
显示剩余8条评论

204

该函数适用于任何操作系统(Unix、Linux、macOS和Windows)
支持Python 2和Python 3

编辑:
@radato替换了os.systemsubprocess.call。这避免了在主机名字符串可能未经过验证的情况下出现shell注入漏洞。

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

请注意,根据@ikrase的说法,在Windows系统上,如果出现“目标主机不可达”错误,则此函数仍将返回True

说明

在Windows和类Unix系统中,命令都是ping
选项-n(Windows)或-c(Unix)控制数据包的数量,在本例中设置为1个。

platform.system()返回平台名称。例如,在macOS上返回'Darwin'
subprocess.call()执行系统调用。例如:subprocess.call(['ls', '-l'])


30
请注意,即使您从不同的主机收到“目标主机不可达”的回复,在Windows上此命令仍将返回true。 - ikrase
1
我发现有时候在我的调制解调器关闭的情况下,我会偶尔得到ping成功的结果???这是在Windows 10操作系统上测试“8.8.8.8”和“google.com”的情况。似乎有些不对劲。 - Markus
@Markus 请看ikrase的评论。 - user3064538
我是不是唯一一个注意到 system.call 是个笔误,应该是 subprocess.call 的人? - ishahak
2
我可以确认,Windows ping 命令的返回值是虚假的。我正在 ping 一个已经从网络中断开连接的系统,另一个 IP 响应说该系统不可用,但我得到了0%的丢失和 ERRORLEVEL 为 0。这里是结果的粘贴 https://pastebin.pl/view/2437bb7c - Joshua
显示剩余8条评论

55

有一个名为pyping的模块可以做到这一点。您可以使用pip进行安装。

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

8
请注意,ICMP消息只能由以root身份运行的进程发送(在Windows中,您必须以“管理员”身份运行此脚本)。 - Ben Hyde
24
无法在Python 3中运行。模块未找到错误:没有名为'core'的模块。 - alireza
2
“core”错误来自于与Python3不兼容。我尝试修复Python3,但它一直给我发送错误。该作者和项目的Github页面已经无法打开(404 Not Found)。我们必须自己将其移植到Python3 :-) - Andre
9
针对Python3,请尝试使用ping3:https://github.com/kyan001/ping3,安装命令为`pip install ping3`。 - beep_check
1
ping3: res = ping('8.8.8.8') 跟踪回溯(最近的调用最先): 文件“<stdin>”,第1行,<module> 文件“/home/env-admin/.local/lib/python3.6/site-packages/ping3.py”,第165行,ping with socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_ICMP) as sock: 文件“/usr/lib/python3.6/socket.py”,第144行,init _socket.socket.init(self, family, type, proto, fileno) 权限错误:[Errno 1] 操作不允许 - xCovelus
显示剩余2条评论

40

对于python3,有一个非常简单和方便的python模块ping3: (pip install ping3,需要root权限)。

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

此模块还允许对一些参数进行自定义。


5
编辑需要 root 权限,讨论如何解决此问题请参阅:https://github.com/kyan001/ping3/issues/10 - Dimitrios Mistriotis
3
需要 root 权限才能安装和执行:ping("example.com")。 - Clock ZHONG
2
这个不需要使用sudo来执行。我正在运行Python 3.8.10版本。 - Amandeep Chugh

36
import subprocess
ping_response = subprocess.Popen(["/bin/ping", "-c1", "-w100", "192.168.0.1"], stdout=subprocess.PIPE).stdout.read()

6
问题在于这种方法不适用于Windows操作系统。 - Kudu
10
需要注意的是,这样做的原因在于ICMP需要root权限,而/bin/ping通过设置SUID来绕过这一限制。 - Catskul
2
注意:如果 ping 不在同一位置可能会失败。使用 whereis ping 获取正确的路径。 - octern
5
这段代码可以在Windows上运行:ping_response = subprocess.Popen(["ping", hostname, "-n", '1'], stdout=subprocess.PIPE).stdout.read() - Victor Lellis
1
如何在Windows中解析结果以检查响应是否为“OK”或“KO”? - Pitto
显示剩余4条评论

30

在Python3中使用socket包:

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

4
不是ICMP,但在无法依赖底层操作系统调用时进行连接测试的绝佳方式。而且非常紧凑。 - NeilG

27

由于发送原始ICMP数据包需要提升特权,而调用ping二进制文件很不方便,因此编写程序化的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连接并立即断开它,测量经过的时间。这个特定的实现有点局限性,因为它不能处理关闭的端口,但对于您自己的服务器来说,它运行得非常好。


这种策略在服务器有防火墙且会屏蔽真实的 ICMP ping 时也非常有用!此外,这是 GitHub 页面链接:https://github.com/zhengxiaowai/tcping - Jerther
如何获取返回值?看起来好像是打印到标准输出(stdout)。 - Vincent Alex
另一个选择的软件包是TCPPingLib - Leon

20

因为我喜欢让我的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"))

3
你当然也可以使用 platform.system().lower() != "windows" 来替代 False if platform.system().lower()=="windows" else True - Frerich Raabe
os.name!="nt" 也可以工作吗?不过我还没有在所有版本/平台组合上尝试过! - Keeely
2
在我的情况下,默认网关返回一个“不可达”的消息,但Windows ping命令仍然具有返回代码0。因此,这种方法有效(对于格式化抱歉-它包括函数声明的6行):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.returncode - womblerone
@wellspokenman 如果在管道中发现了 unreachable,你更愿意返回 0 吗? - beeb
1
@beeb 是的,我也这样做了,但忘记更新注释了。我的当前函数看起来像这样:https://pastebin.com/FEYWsVjK - womblerone
感谢@wellspokenman,直到我尝试了你在pastebin.com上发布的解决方案,这里几乎没有任何东西在Windows下能够正确地为我工作。非常感谢你。它也非常快速。 - MKANET

14

我的ping函数版本:

  • 能在Python 3.5及之后的版本和Windows、Linux系统上工作。
  • 在Windows系统上,如果ping命令返回“Destination Host Unreachable”,则返回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

随意使用,任何用途皆可。


超时时间以秒为单位计算,而不是毫秒,因此默认值1000不太有用。请将其编辑为“1”。 - ratijas
1
@ratijas 超时参数必须以毫秒为单位在Windows和Linux中传递。我刚刚查找了Mac OS命令,它使用秒; 但无论如何,我无法在Mac上测试它。 - Jose Francisco Lopez Pimentel

13

在寻找了一番后,我最终编写了自己的ping模块,它旨在监控大量的地址,采用异步方式,并且不占用太多系统资源。您可以在这里找到它:https://github.com/romana/multi-ping/ 它是Apache许可证,因此您可以按照任何方式在项目中使用它。

实现自己的主要原因是其他方法的限制:

  • 这里提到的许多解决方案都需要执行命令行实用程序。如果您需要监控大量IP地址,则这种方法效率低下且资源消耗大。
  • 其他人提到了一些较旧的Python ping模块。我查看了这些模块,最终它们都存在一些问题(例如未正确设置数据包ID)并且不能处理大量地址的ping操作。

干得好,伙计!如果有人想看它的实际效果,只需使用https://github.com/romana/multi-ping/blob/master/demo.py。 - Cucu
1
值得一提的是,这也需要 root 权限。 - Jann Poppinga

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接