Python实现ping指定IP

在计算机网络中,ping是一种常用的网络诊断工具,用于测试目标主机是否能够联通。Ping通常使用ICMP协议来发送网络数据包,并通过接收目标主机返回的数据包来判断网络是否正常。在本文中,我们将使用Python来实现ping指定IP的功能。

ICMP协议简介

ICMP(Internet Control Message Protocol)是互联网控制报文协议,用于在IP网络中传输控制信息。Ping使用的是ICMP Echo Request和Echo Reply消息。当一个主机收到Echo Request消息后,会立即返回一个Echo Reply消息作为响应。通过这个过程,我们可以判断目标主机是否能够联通。

Python实现

要实现ping指定IP的功能,我们可以使用Python中的socket模块来发送和接收ICMP消息。下面是一个简单的Python代码示例:

import os
import socket
import struct
import select
import time

ICMP_ECHO_REQUEST = 8  # ICMP Echo Request类型
DEFAULT_TIMEOUT = 2  # 默认超时时间,单位为秒
DEFAULT_COUNT = 4  # 默认ping的次数

# 计算校验和
def checksum(source_string):
    sum = 0
    count_to = (len(source_string) // 2) * 2
    count = 0
    while count < count_to:
        this_val = source_string[count + 1] * 256 + source_string[count]
        sum = sum + this_val
        sum = sum & 0xffffffff
        count = count + 2
    if count_to < len(source_string):
        sum = sum + source_string[len(source_string) - 1]
        sum = sum & 0xffffffff
    sum = (sum >> 16) + (sum & 0xffff)
    sum = sum + (sum >> 16)
    answer = ~sum
    answer = answer & 0xffff
    answer = answer >> 8 | (answer << 8 & 0xff00)
    return answer

# 发送ICMP消息
def send_icmp_request(dest_addr, icmp_id, icmp_seq):
    sock = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.getprotobyname("icmp"))
    sock.setsockopt(socket.SOL_IP, socket.IP_TTL, 255)
    icmp_header = struct.pack("!BBHHH", ICMP_ECHO_REQUEST, 0, 0, icmp_id, icmp_seq)
    icmp_checksum = checksum(icmp_header)
    icmp_header = struct.pack("!BBHHH", ICMP_ECHO_REQUEST, 0, icmp_checksum, icmp_id, icmp_seq)
    send_time = time.time()
    sock.sendto(icmp_header, (dest_addr, 1))
    return send_time, sock

# 接收ICMP消息
def receive_icmp_reply(sock, icmp_id, timeout):
    while True:
        select_start = time.time()
        ready = select.select([sock], [], [], timeout)
        select_duration = time.time() - select_start
        if ready[0] == []:  # 超时
            return None
        receive_time = time.time()
        packet_data, addr = sock.recvfrom(1024)
        icmp_header = packet_data[20:28]
        icmp_type, code, checksum, packet_id, sequence = struct.unpack("!BBHHH", icmp_header)
        if icmp_type == 0 and packet_id == icmp_id:  # Echo Reply
            return receive_time - select_start

# ping指定IP
def ping(dest_addr, timeout=DEFAULT_TIMEOUT, count=DEFAULT_COUNT):
    print(f"Pinging {dest_addr} with {count} bytes of data:")
    for i in range(count):
        icmp_id = os.getpid() & 0xFFFF
        icmp_seq = i
        send_time, sock = send_icmp_request(dest_addr, icmp_id, icmp_seq)
        duration = receive_icmp_reply(sock, icmp_id, timeout)
        if duration is None:
            print(f"Request timed out.")
        else:
            print(f"Reply from {dest_addr}: bytes=32 time={int(duration * 1000)}ms")
        time.sleep(1)
    sock.close()

if __name__ == "__main__":
    ping("127.0.0.1")

代码解析:

  • 首先定义了ICMP Echo Request的类型码为8,超时时间和ping的次数的默认值。
  • checksum函数用于计算ICMP消息的校验和,这是ICMP协议的要求。
  • send_icmp_request函数用于发送ICMP Echo Request消息,并返回发送时间和socket对象。
  • receive_icmp_reply函数用于接收ICMP Echo Reply消息,并返回往返时延。
  • ping函数用于执行ping指