Python-web-communicate/代码解释.md
2024-12-08 23:15:32 +08:00

6.4 KiB
Raw Blame History

下面是任务 2 代码的逐行详细解释:


import tkinter as tk
from tkinter import messagebox
import socket
import struct
import re
import time
  1. tkinterPython 的内置 GUI 库,用于创建图形用户界面。
  2. messageboxtkinter 模块中的组件,用于显示错误或提示信息。
  3. socket:提供网络编程接口,用于构造和发送报文。
  4. struct:用于将数据打包成特定格式(二进制结构),例如构造报文头。
  5. re:正则表达式模块,用于验证 IP 地址的格式。
  6. time:用于记录和计算时间,测量报文往返的时间。

构造 ICMP 报文

def construct_icmp():
    icmp_header = struct.pack('!BBHHH', 8, 0, 0, 1, 1)
    checksum = ~sum(icmp_header) & 0xFFFF
    icmp_header = struct.pack('!BBHHH', 8, 0, checksum, 1, 1)
    return icmp_header
  1. 构造 ICMP 报文的头部
    • 使用 struct.pack 打包数据为二进制格式。
    • ! 表示网络字节序(大端模式)。
    • BBHHH 定义字段类型2 个 8 位整数3 个 16 位整数。
      • 8 表示 ICMP 类型8 = 回显请求)。
      • 0 表示代码字段。
      • 0 表示校验和(暂时填充 0
      • 1, 1 表示标识符和序列号。
  2. 计算校验和
    • 使用 ~sum(...) 计算报文的校验和并取反,确保数据完整性。
    • 结果为 16 位,使用 & 0xFFFF 截断为无符号整数。
  3. 重新打包报文
    • 将计算后的校验和填入头部。
  4. 返回构造好的 ICMP 报文

构造 UDP 报文

def construct_udp(src_port, dest_port):
    udp_header = struct.pack('!HHHH', src_port, dest_port, 8, 0)
    return udp_header
  1. 打包 UDP 报文头
    • HHHH 表示 4 个 16 位整数(源端口、目标端口、长度、校验和)。
      • src_port:源端口号。
      • dest_port:目标端口号。
      • 8UDP 报文长度(仅头部时固定为 8 字节)。
      • 0:校验和(暂时不计算,系统会自动填充)。
  2. 返回 UDP 报文头

验证 IP 地址

def validate_ip(ip):
    pattern = re.compile(r'^\d{1,3}(\.\d{1,3}){3}$')
    return pattern.match(ip) is not None
  1. 正则表达式验证
    • 匹配 IP 地址格式:四段数字,每段 0-255 范围。
    • ^\d{1,3}:每段由 1 到 3 位数字组成。
    • (\.\d{1,3}){3}:重复三次的“点+数字”模式。
  2. 返回结果
    • 如果匹配成功,返回 True
    • 如果失败,返回 False

发送报文

def send_packet():
    try:
        target_ip = ip_entry.get()
        target_port = int(port_entry.get())
        packet_type = var.get()
        if not validate_ip(target_ip):
            raise ValueError("目标 IP 地址无效")
  1. 获取用户输入
    • ip_entry.get():从文本框中获取目标 IP 地址。
    • port_entry.get():从文本框中获取目标端口号并转为整数。
    • var.get():从单选框中获取选择的报文类型。
  2. 验证 IP 地址
    • 使用 validate_ip 函数检查 IP 格式。
    • 如果无效,抛出 ValueError 异常。

        sock = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_ICMP if packet_type == "ICMP" else socket.IPPROTO_UDP)
  1. 创建套接字
    • socket.AF_INET:使用 IPv4 地址。
    • socket.SOCK_RAW:使用原始套接字,允许手动构造报文。
    • socket.IPPROTO_ICMPICMP 协议。
    • socket.IPPROTO_UDPUDP 协议。

        if packet_type == "ICMP":
            packet = construct_icmp()
        elif packet_type == "UDP":
            packet = construct_udp(12345, target_port)
  1. 选择报文类型
    • 如果选择 ICMP则调用 construct_icmp 构造报文。
    • 如果选择 UDP则调用 construct_udp,源端口固定为 12345。

        start_time = time.time()
        sock.sendto(packet, (target_ip, target_port))
        end_time = time.time()
  1. 发送报文并测量时间
    • 记录发送开始时间 start_time
    • 使用 sendto 方法将报文发送到目标地址和端口。
    • 记录发送结束时间 end_time

        result.set(f"报文发送成功!类型: {packet_type}, 往返时间: {end_time - start_time:.2f} 秒")
    except Exception as e:
        messagebox.showerror("错误", f"报文发送失败: {e}")
  1. 显示结果
    • 使用 result.set 更新界面上的结果显示。
    • 如果发送成功,显示报文类型和往返时间。
  2. 错误处理
    • 捕获异常并通过 messagebox 提示错误信息。

GUI 界面

window = tk.Tk()
window.title("网络通信软件")

tk.Label(window, text="目标 IP 地址:").grid(row=0, column=0)
ip_entry = tk.Entry(window)
ip_entry.grid(row=0, column=1)

tk.Label(window, text="目标端口号:").grid(row=1, column=0)
port_entry = tk.Entry(window)
port_entry.grid(row=1, column=1)

tk.Label(window, text="报文类型:").grid(row=2, column=0)
var = tk.StringVar(value="ICMP")
tk.Radiobutton(window, text="ICMP", variable=var, value="ICMP").grid(row=2, column=1)
tk.Radiobutton(window, text="UDP", variable=var, value="UDP").grid(row=2, column=2)

result = tk.StringVar()
tk.Label(window, textvariable=result).grid(row=4, column=0, columnspan=3)

tk.Button(window, text="发送报文", command=send_packet).grid(row=3, column=1)
window.mainloop()
  1. 窗口初始化
    • 创建 window 窗口对象。
    • 设置窗口标题为“网络通信软件”。
  2. 界面布局
    • 使用 grid 方法设置控件布局,分别添加:
      • 文本标签:提示用户输入目标 IP 和端口号。
      • 输入框:用于输入目标 IP 和端口号。
      • 单选框选择报文类型ICMP 或 UDP
      • 按钮:点击后调用 send_packet 函数发送报文。
      • 结果标签:显示发送结果或错误信息。
  3. 事件循环
    • window.mainloop() 启动 GUI 主循环,等待用户交互。

总结

这段代码实现了一个功能完备的网络通信工具,具备以下特点:

  • GUI 界面:直观的输入与反馈。
  • 多协议支持:可发送 ICMP 和 UDP 报文。
  • 输入验证:保证用户输入的有效性。
  • 异常处理:确保程序运行稳定。