python: socket与命令提示符的使用

Q: 使用Python实现Ping,可传入参数timeout,count。要求能将每个Ping包能否Ping的结果及发送时间记录在文件中最后的统计结果需给出丢包率、最小Ping通时间、最大Ping通时间、平均Ping通时间。要求:至少使用2种不同的实现方式.
分析:实现ping, 翻译一下就是实现网络层协议
第一种方法是创建一个进程(这里的话socket是网络套接字,最好看一下《网络是怎样连接的》,复习一下套接字的基础内容,结合python代码以及Python中socket库的使用,是较为合适的方法):创建进程之后,调用系统的ping命令,这应该不是合格的做法。

import subprocess
import re
import statistics

def ping_with_subprocess(host, timeout=1, count=4):
    ping_result = subprocess.run(['ping', '-c', str(count), '-W', str(timeout), host], capture_output=True, text=True)
    output = ping_result.stdout

    # 解析 ping 结果
    packets = re.findall(r'(\d+) packets transmitted, (\d+) received', output)
    if packets:
        transmitted = int(packets[0][0])
        received = int(packets[0][1])
        lost = transmitted - received
        loss_rate = (lost / transmitted) * 100

        rtt_times = re.findall(r'time=(\d+\.\d+) ms', output)
        rtt_times = [float(time) for time in rtt_times]
        min_rtt = min(rtt_times) if rtt_times else None
        max_rtt = max(rtt_times) if rtt_times else None
        avg_rtt = statistics.mean(rtt_times) if rtt_times else None

        # 将结果写入文件
        with open('ping_result.txt', 'a') as file:
            file.write(f'Host: {host}\n')
            file.write(f'Transmitted: {transmitted}\n')
            file.write(f'Received: {received}\n')
            file.write(f'Lost: {lost}\n')
            file.write(f'Loss rate: {loss_rate}%\n')
            file.write(f'Min RTT: {min_rtt} ms\n')
            file.write(f'Max RTT: {max_rtt} ms\n')
            file.write(f'Average RTT: {avg_rtt} ms\n')
            file.write('---\n')

        return loss_rate, min_rtt, max_rtt, avg_rtt
    else:
        return None, None, None, None


# 示例用法
host = 'www.google.com'
timeout = 1
count = 4

ping_with_subprocess(host, timeout, count)

第二种方法是:使用socket模块发送ICMP Echo请求【为什么不能实现Ping的原理呢?】

import socket
import struct
import time
import statistics

def ping_with_socket(host, timeout=1, count=4):
    icmp = socket.getprotobyname('icmp')
    sock = socket.socket(socket.AF_INET, socket.SOCK_RAW, icmp)
    sock.settimeout(timeout)

    seq_number = 0
    transmitted = 0
    received = 0
    rtt_times = []

    # 发送 ICMP Echo 请求
    for i in range(count):
        seq_number += 1
        icmp_header = struct.pack('!BBHHH', 8, 0, 0, seq_number, 1)
        checksum = calculate_checksum(icmp_header)
        icmp_header = struct.pack('!BBHHH', 8, 0, checksum, seq_number, 1)

        start_time = time.time()
        try:
            sock.sendto(icmp_header, (host, 0))
            transmitted += 1

            # 接收 ICMP Echo 回复
            recv_packet, addr = sock.recvfrom(1024)
            end_time = time.time()
            rtt = (end_time - start_time) * 1000
            rtt_times.append(rtt)
            received += 1

            # 将结果写入文件
            with open('ping_result.txt', 'a') as file:
                file.write(f'Host: {host}\n')
                file.write(f'Sequence number: {seq_number}\n')
                file.write(f'RTT: {rtt} ms\n')
                file.write('---\n')

        except socket.timeout:
            # 超时未收到回复
            with open('ping_result.txt', 'a') as file:
                file.write(f'Host: {host}\n')
                file.write(f'Sequence number: {seq_number}\n')
                file.write('Timeout\n')
                file.write('---\n')

    sock.close()

    lost = transmitted - received
    loss_rate = (lost / transmitted) * 100
    min_rtt = min(rtt_times) if rtt_times else None
    max_rtt = max(rtt_times) if rtt_times else None
    avg_rtt = statistics.mean(rtt_times) if rtt_times else None

    # 将统计结果写入文件
    with open('ping_result.txt',

第三种方法是使用ping3,但是还是调包,与第一种方法是一样的。
如果是通过ping的原理,那就与网络套接字相关了:

import os
import sys
import struct
import socket
import select
import time

ICMP_ECHO_REQUEST = 8  # ICMP Echo Request类型码

def checksum(source_string):
    """
    计算校验和
    """
    sum = 0
    countTo = (len(source_string) / 2) * 2
    count = 0
    while count < countTo:
        thisVal = ord(source_string[count + 1]) * 256 + ord(source_string[count])
        sum = sum + thisVal
        sum = sum & 0xffffffff  # 处理溢出
        count = count + 2

    if countTo < len(source_string):
        sum = sum + ord(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

def receive_ping(my_socket, ID, timeout):
    """
    接收Ping回复报文
    """
    timeLeft = timeout
    while True:
        startedSelect = time.time()
        whatReady = select.select([my_socket], [], [], timeLeft)
        howLongInSelect = (time.time() - startedSelect)
        if whatReady[0] == []:  # 超时
            return

        timeReceived = time.time()
        recPacket, addr = my_socket.recvfrom(1024)

        # 解析ICMP报文
        icmpHeader = recPacket[20:28]
        type, code, checksum, packetID, sequence = struct.unpack("bbHHh", icmpHeader)

        if packetID == ID:
            bytesInDouble = struct.calcsize("d")
            timeSent = struct.unpack("d", recPacket[28:28 + bytesInDouble])[0]
            return timeReceived - timeSent

        timeLeft = timeLeft - howLongInSelect
        if timeLeft <= 0:
            return

def send_ping(my_socket, dest_addr, ID):
    """
    发送Ping请求报文
    """
    dest_addr = socket.gethostbyname(dest_addr)

    # 构建ICMP报文
    my_checksum = 0
    header = struct.pack("bbHHh", ICMP_ECHO_REQUEST, 0, my_checksum, ID, 1)
    bytesInDouble = struct.calcsize("d")
    data = (192 - bytesInDouble) * "Q"
    data = struct.pack("d", time.time()) + data

    # 计算校验和
    my_checksum = checksum(header + data)

    # 重新构建ICMP报文,加入校验和
    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 ping(dest_addr, timeout=1, count=4):
    """
    Ping函数
    """
    try:
        my_socket = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.getprotobyname("icmp"))
    except socket.error as e:
        print("Socket创建失败: %s" % e)
        sys.exit()

    ID = os.getpid() & 0xFFFF  # 用当前进程ID作为标识符

    for i in range(count):
        send_ping(my_socket, dest_addr, ID)
        delay = receive_ping(my_socket, ID, timeout)
        if delay is None:
            print("Ping %s 超时" % dest_addr)
        else:
            print("Ping %s 回复,延迟=%.3fms" % (dest_addr, delay * 1000))

    my_socket.close()

# 示例用法
ping("www.google.com")

使用socket创建一个原始套接字,构建ICMP报文并发送给目标主机,并且可以接收到回复报文。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值