update beta main

This commit is contained in:
fly6516 2024-12-08 23:15:32 +08:00
commit e8e2953750
7 changed files with 305 additions and 0 deletions

8
.idea/.gitignore vendored Normal file
View File

@ -0,0 +1,8 @@
# 默认忽略的文件
/shelf/
/workspace.xml
# 基于编辑器的 HTTP 客户端请求
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="PYTHON_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/.venv" />
</content>
<orderEntry type="jdk" jdkName="Python 3.12 (Python-web-communicate)" jdkType="Python SDK" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

View File

@ -0,0 +1,6 @@
<component name="InspectionProjectProfileManager">
<settings>
<option name="USE_PROJECT_PROFILE" value="false" />
<version value="1.0" />
</settings>
</component>

8
.idea/modules.xml Normal file
View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/Python-web-communicate.iml" filepath="$PROJECT_DIR$/.idea/Python-web-communicate.iml" />
</modules>
</component>
</project>

6
.idea/vcs.xml Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

76
main.py Normal file
View File

@ -0,0 +1,76 @@
import tkinter as tk
from tkinter import messagebox
import socket
import struct
import re
import 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
# 构造 UDP 报文
def construct_udp(src_port, dest_port):
udp_header = struct.pack('!HHHH', src_port, dest_port, 8, 0)
return udp_header
# 验证输入
def validate_ip(ip):
pattern = re.compile(r'^\d{1,3}(\.\d{1,3}){3}$')
return pattern.match(ip) is not None
# 发送报文
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 地址无效")
sock = socket.socket(socket.AF_INET, socket.SOCK_RAW,
socket.IPPROTO_ICMP if packet_type == "ICMP" else socket.IPPROTO_UDP)
if packet_type == "ICMP":
packet = construct_icmp()
elif packet_type == "UDP":
packet = construct_udp(12345, target_port)
start_time = time.time()
sock.sendto(packet, (target_ip, target_port))
end_time = time.time()
result.set(f"报文发送成功!类型: {packet_type}, 往返时间: {end_time - start_time:.2f}")
except Exception as e:
messagebox.showerror("错误", f"报文发送失败: {e}")
# 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()

191
代码解释.md Normal file
View File

@ -0,0 +1,191 @@
下面是任务 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 报文。
- **输入验证**:保证用户输入的有效性。
- **异常处理**:确保程序运行稳定。