博客
关于我
Python ping 模块
阅读量:798 次
发布时间:2023-03-06

本文共 6612 字,大约阅读时间需要 22 分钟。

使用socket模块实现纯Python的ping工具

在网络监控领域,了解服务器的网络状态是非常重要的。通过socket模块,我们可以轻松实现从命令行或脚本中获取域名对应的IP地址。以下是详细的实现方案。


1. 基于socket的ping工具

我们可以通过socket模块在Python中实现一个简单的ping工具。以下是一个基本的实现示例:

import socketdef ping(host):    try:        ip = socket.gethostbyname(host)        print(f"Host {host} resolved to IP: {ip}")    except socket.gaierror:        print(f"Could not resolve host: {host}")

2. 使用fping批量ping多个IP地址

当需要批量检查大量服务器的网络状态时,fping是一个非常有用的工具。它能够在瞬间完成对多个IP地址的ping操作,只有当某个IP地址无法连接时,才会逐步增加延迟时间。以下是一个简单的fping脚本示例:

#!/bin/bashrm -f result.txtcat ip_list.txt | fping > result.txt

3. 自动化ping检查与数据库更新

为了实现自动化,我们可以将IP地址存储在数据库中,并通过脚本定期执行ping检查。以下是一个Python脚本示例:

import subprocessimport osdef check_online_status(ip_list):    if os.path.exists('iplist.txt'):        os.remove('iplist.txt')        with open('iplist.txt', 'a') as f:        for ip in ip_list:            f.write(f"{ip}\n")        p = subprocess.Popen(r'./fping.sh', stdout=subprocess.PIPE)    p.stdout.read()        result_file = 'result.txt'    with open(result_file, 'r') as f:        content = f.read().split('\n')        for line in content:            if not line:                continue            parts = line.split()            ip = parts[0]            status = 0            if 'unreachable' in line:                status = 1            # 更新数据库状态            cmd = f"update ip_check set status={status} where ip='{ip}'"            os.system(cmd)def main():    # 从数据库中获取IP列表    ip_list = mysql('select ip from ip_check')    check_online_status(ip_list)    print('所有IP状态检查完毕!')if __name__ == '__main__':    main()

4. 使用脚本定期运行

为了实现自动化,我们可以将脚本设置为计划任务定期运行。以下是如何在Linux上设置计划任务的示例:

chmod +x ping_check.sh*/5 * * * * /path/to/ping_check.sh

5. 完整的实现代码

以下是完整的Python实现代码,包含了verbose模式和quiet模式:

#!/usr/bin/env python"""A pure Python ping implementation using raw socket.Note that ICMP messages can only be sent from processes running as root.Derived from ping.c distributed in Linux's netkit."""import osimport selectimport socketimport structimport sysimport time# ICMP类型标识符ICMP_ECHO_REQUEST = 8def checksum(source_string):    """Calculates the checksum of the given string."""    sum = 0    count_to = (len(source_string) // 2) * 2    for count in range(0, count_to, 2):        this = ord(source_string[count + 1]) * 256 + ord(source_string[count])        sum += this        sum &= 0xFFFFFFFF  # 操作符位宽和    if count_to < len(source_string):        sum += ord(source_string[len(source_string) - 1])        sum &= 0xFFFFFFFF    sum = (sum >> 16) + (sum & 0xFFFF)    sum = sum + (sum >> 16)    answer = ~sum    answer &= 0xFFFF    # 交换字节    answer = (answer >> 8) | (answer << 8 & 0xFF00)    return answerdef receive_one_ping(my_socket, id, timeout):    """Receives a ping response from the socket."""    time_left = timeout    while True:        started_select = time.time()        what_ready = select.select([my_socket], [], [], time_left)        how_long_in_select = time.time() - started_select        if not what_ready[0]:            return None        time_received = time.time()        received_packet, addr = my_socket.recvfrom(1024)        if not received_packet:            continue        icmpHeader = received_packet[20:28]        type, code, checksum, packet_id, sequence = struct.unpack(            "bbHHh", icmpHeader        )        if packet_id == id:            bytes = struct.calcsize("d")            time_sent = struct.unpack("d", received_packet[28:28 + bytes])[0]            return time_received - time_sent        time_left = time_left - how_long_in_select        if time_left <= 0:            return Nonedef send_one_ping(my_socket, dest_addr, id, psize):    """Sends a ping to the specified address."""    dest_addr = socket.gethostbyname(dest_addr)    psize -= 8  # ICMP头部占用8字节    my_checksum = 0    # 创建一个带有0校验和的头部    header = struct.pack("bbHHh", ICMP_ECHO_REQUEST, 0, my_checksum, id, 1)    data = (psize - struct.calcsize("d")) * "Q"    timestamp = struct.pack("d", time.time())    data = timestamp + data    # 计算完整的校验和    my_checksum = checksum(header + data)    # 创建最终的头部    header = struct.pack(        "bbHHh",         ICMP_ECHO_REQUEST,         0,         socket.htons(my_checksum),         id,         1    )    packet = header + data    my_socket.sendto(packet, (dest_addr, 1))def do_one(dest_addr, timeout, psize):    """Performs a single ping test and returns the delay."""    icmp = socket.getprotobyname("icmp")    try:        my_socket = socket.socket(socket.AF_INET, socket.SOCK_RAW, icmp)    except socket.error as e:        if e.errno == 1:            raise socket.error("ICMP messages can only be sent from processes running as root.")        raise    my_id = os.getpid() & 0xFFFF    send_one_ping(my_socket, dest_addr, my_id, psize)    my_socket.close()    return receive_one_ping(my_socket, my_id, timeout)def verbose_ping(dest_addr, timeout=2, count=4, psize=64):    """Sends `count` pings to `dest_addr` and displays the results."""    for i in range(count):        print(f"ping {dest_addr} with ...")        try:            delay = do_one(dest_addr, timeout, psize)        except socket.gaierror as e:            print(f"failed. (socket error: {e[1]})")            break        if delay is None:            print(f"failed. (timeout within {timeout} seconds)")        else:            delay *= 1000            print(f"get ping in {delay:.4f}ms")    print()def quiet_ping(dest_addr, timeout=2, count=4, psize=64):    """Sends `count` pings to `dest_addr` and returns statistics."""    lost = 0    plist = []    for i in range(count):        try:            delay = do_one(dest_addr, timeout, psize)            delay *= 1000            plist.append(delay)        except socket.gaierror as e:            print(f"failed. (socket error: {e[1]})")            break    percent_lost = 100 - (len(plist) * 100 // count)    if plist:        max_rtt = max(plist)        avg_rtt = sum(plist) / len(plist)    else:        max_rtt = None        avg_rtt = None    print(f"{percent_lost}% packets lost")    if max_rtt is not None:        print(f"Max RTT: {max_rtt:.2f}ms")        print(f"Avg RTT: {avg_rtt:.2f}ms")    print(len(plist))if __name__ == '__main__':    # verbose_ping("heise.de")    # verbose_ping("google.com")    # verbose_ping("a-test-url-taht-is-not-available.com")    verbose_ping("www.xd.com")    print(quiet_ping("www.xd.com", count=10))

6. 代码说明

  • 主要功能

    • verbose_ping:显示详细的ping结果。
    • quiet_ping:返回丢包率、最大延迟和平均延迟。
  • 适用场景

    • 当心服务器数量较多时,使用fping进行批量ping更为高效。
    • 当ping不通时,fping会逐步增加延迟,确保大部分时间都能快速得到结果。
  • 注意事项

    • Python 3.6及以上版本可能会对部分语法有所限制,建议在PyCharm中使用autopep8进行格式化。
    • 由于使用了低级别的socket操作,需要确保脚本有权限执行(通常需要root权限)。

  • 通过以上方法,我们可以轻松实现网络状态监控,适用于服务器数量较多的场景。

    转载地址:http://smafk.baihongyu.com/

    你可能感兴趣的文章
    Python - DM 一个用户 Discord 机器人
    查看>>
    python - Flask 基础 - 蓝图( Blueprint )(2)
    查看>>
    python - os.getenv 和 os.environ 看不到我的 bash shell 的环境变量
    查看>>
    Python - while循环
    查看>>
    Python - “if“中的逻辑评估顺序陈述
    查看>>
    Python - 使用 pandas 格式化 Excel 单元格
    查看>>
    python - 列表
    查看>>
    Python - 可用时从串行端口数据逐行读取到列表中
    查看>>
    Python - 在应用程序中显示 Web 浏览器/iframe
    查看>>
    Python - 如何使用管道执行shell命令,但没有‘shell = True‘?
    查看>>
    Python - 如何使这个不可腌制的对象可腌制?
    查看>>
    Python - 如何在 Windows 10 上完全卸载 Anaconda?
    查看>>
    python - 如何并行化python numpy中的总和计算?
    查看>>
    Python - 如何解析 xml 响应并将元素值存储在变量中?
    查看>>
    Python - 安装了扩展的远程 Webdriver
    查看>>
    python - 将字符串中的日期与今天的日期进行比较
    查看>>
    python - 数据描述符(class 内置 get/set/delete方法 )
    查看>>
    Python - 根据值绘制彩色网格
    查看>>
    Python - 正则表达式在括号之间获取数字
    查看>>
    Python - 正则表达式在括号之间获取数字
    查看>>