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

191 lines
6.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

下面是任务 2 代码的逐行详细解释:
---
```python
import tkinter as tk
from tkinter import messagebox
import socket
import struct
import re
import time
```
1. **`tkinter`**Python 的内置 GUI 库,用于创建图形用户界面。
2. **`messagebox`**`tkinter` 模块中的组件,用于显示错误或提示信息。
3. **`socket`**:提供网络编程接口,用于构造和发送报文。
4. **`struct`**:用于将数据打包成特定格式(二进制结构),例如构造报文头。
5. **`re`**:正则表达式模块,用于验证 IP 地址的格式。
6. **`time`**:用于记录和计算时间,测量报文往返的时间。
---
### 构造 ICMP 报文
```python
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 报文
```python
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**:目标端口号。
- **8**UDP 报文长度(仅头部时固定为 8 字节)。
- **0**:校验和(暂时不计算,系统会自动填充)。
2. **返回 UDP 报文头**
---
### 验证 IP 地址
```python
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`
---
### 发送报文
```python
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` 异常。
---
```python
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_ICMP`ICMP 协议。
- `socket.IPPROTO_UDP`UDP 协议。
---
```python
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。
---
```python
start_time = time.time()
sock.sendto(packet, (target_ip, target_port))
end_time = time.time()
```
1. **发送报文并测量时间**
- 记录发送开始时间 `start_time`
- 使用 `sendto` 方法将报文发送到目标地址和端口。
- 记录发送结束时间 `end_time`
---
```python
result.set(f"报文发送成功!类型: {packet_type}, 往返时间: {end_time - start_time:.2f} 秒")
except Exception as e:
messagebox.showerror("错误", f"报文发送失败: {e}")
```
1. **显示结果**
- 使用 `result.set` 更新界面上的结果显示。
- 如果发送成功,显示报文类型和往返时间。
2. **错误处理**
- 捕获异常并通过 `messagebox` 提示错误信息。
---
### GUI 界面
```python
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 报文。
- **输入验证**:保证用户输入的有效性。
- **异常处理**:确保程序运行稳定。