Python-web-communicate/main.py
fly6516 71a8e81f68 docs: 添加原理解释和校验和计算文档
- 新增原理解释文档,详细说明了 ICMP、UDP 和 TCP 报文的构建与发送原理
- 新增校验和计算文档,提供了正确的 ICMP 校验和计算方法
- 这些文档有助于开发者更好地理解网络报文的构建和发送过程
2025-01-06 15:07:59 +08:00

258 lines
7.5 KiB
Python
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.

import tkinter as tk
from tkinter import ttk, messagebox
import socket
import struct
import time
import uuid
import re
# 获取本机信息
def get_local_info():
"""
获取本机的主机名、MAC地址和IP地址列表。
Returns:
tuple: 包含主机名、MAC地址和IP地址列表的元组。
"""
hostname = socket.gethostname()
local_ips = socket.gethostbyname_ex(hostname)[-1]
mac = ':'.join(re.findall('..', '%012x' % uuid.getnode()))
return hostname, mac, local_ips
# 验证 IP 地址或域名
def validate_ip_or_domain(value):
"""
验证输入值是否为有效的IP地址或域名。
Args:
value (str): 待验证的IP地址或域名。
Returns:
bool: 如果输入值有效返回True否则返回False。
"""
ip_pattern = re.compile(r'^\d{1,3}(\.\d{1,3}){3}$')
domain_pattern = re.compile(r'^([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}$')
return ip_pattern.match(value) or domain_pattern.match(value)
# 验证端口号
def validate_port(port):
"""
验证输入的端口号是否有效。
Args:
port (str): 待验证的端口号。
Returns:
bool: 如果端口号有效返回True否则返回False。
"""
return port.isdigit() and 1 <= int(port) <= 65535
# 构造 IP 头部
def construct_ip_header(src_ip, dest_ip, payload_length, protocol):
"""
构造IP头部。
Args:
src_ip (str): 源IP地址。
dest_ip (str): 目标IP地址。
payload_length (int): 负载长度。
protocol (int): 协议号。
Returns:
bytes: 构造的IP头部。
"""
version = 4
ihl = 5
tos = 0
total_length = 20 + payload_length
identification = 54321
flags_offset = 0
ttl = 64
header_checksum = 0
src_ip_bytes = socket.inet_aton(src_ip)
dest_ip_bytes = socket.inet_aton(dest_ip)
ip_header = struct.pack('!BBHHHBBH4s4s',
(version << 4) + ihl, tos, total_length,
identification, flags_offset, ttl, protocol,
header_checksum, src_ip_bytes, dest_ip_bytes)
return ip_header
# 构造 ICMP 报文
def construct_icmp():
"""
构造ICMP报文。
Returns:
bytes: 构造的ICMP报文。
"""
# ICMP 报文头部: 类型(8), 代码(0), 校验和(0), 标识符(1), 序列号(1)
icmp_header = struct.pack('!BBHHH', 8, 0, 0, 1, 1)
data = b'Ping'
# 计算校验和
checksum = calculate_checksum(icmp_header + data)
# 填充校验和字段
icmp_header = struct.pack('!BBHHH', 8, 0, checksum, 1, 1)
return icmp_header + data
# 构造 UDP 报文
def construct_udp(src_port, dest_port):
"""
构造UDP报文。
Args:
src_port (int): 源端口号。
dest_port (int): 目标端口号。
Returns:
bytes: 构造的UDP报文。
"""
udp_header = struct.pack('!HHHH', src_port, dest_port, 8, 0)
return udp_header
# 构造 TCP 报文SYN
def construct_tcp(src_port, dest_port):
"""
构造TCP报文SYN
Args:
src_port (int): 源端口号。
dest_port (int): 目标端口号。
Returns:
bytes: 构造的TCP报文。
"""
seq = 0
ack_seq = 0
offset = 5
reserved = 0
flags = 0b000010 # SYN flag
window = socket.htons(5840)
checksum = 0
urgent_ptr = 0
tcp_header = struct.pack('!HHLLBBHHH',
src_port, dest_port, seq, ack_seq,
(offset << 4) + reserved, flags, window,
checksum, urgent_ptr)
return tcp_header
# 计算校验和
def calculate_checksum(data):
"""
计算校验和。
Args:
data (bytes): 待计算校验和的数据。
Returns:
int: 计算得到的校验和。
"""
checksum = 0
n = len(data)
# 按 16 位块划分
for i in range(0, n - 1, 2):
chunk = (data[i] << 8) + data[i + 1]
checksum += chunk
# 如果长度为奇数,补零
if n % 2 == 1:
checksum += data[-1] << 8
# 将 32 位总和折叠为 16 位
checksum = (checksum >> 16) + (checksum & 0xFFFF)
checksum += (checksum >> 16)
# 对总和取反
return ~checksum & 0xFFFF
# 发送报文
def send_packet():
"""
发送报文。
"""
try:
src_ip = ip_combobox.get()
dest_host = dest_entry.get()
dest_port = port_entry.get()
packet_type = var.get()
# 验证目标地址
if not validate_ip_or_domain(dest_host):
raise ValueError("目标地址无效,必须为有效的 IP 或域名")
# 验证端口号
if not validate_port(dest_port):
raise ValueError("端口号无效必须是有效的正整数1-65535")
dest_ip = socket.gethostbyname(dest_host)
src_port = 12345
dest_port = int(dest_port)
# 构造报文并发送
if packet_type == "ICMP":
sock = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_ICMP)
packet = construct_icmp()
elif packet_type == "UDP":
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
packet = construct_udp(src_port, dest_port)
elif packet_type == "TCP":
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((dest_ip, dest_port))
packet = b"Hello, TCP!" # TCP 是面向连接的,这里发送简单的字符串
sock.sendall(packet)
sock.close()
result.set(f"TCP 连接成功,数据已发送!")
return # 结束函数
# 发送报文
sock.sendto(packet, (dest_ip, dest_port))
result.set(f"报文发送成功!类型: {packet_type}")
except socket.gaierror:
messagebox.showerror("网络错误", "无法解析目标地址,请检查输入的 IP 或域名是否正确。")
except socket.error as e:
messagebox.showerror("网络错误", f"网络错误: {e.strerror}")
except ValueError as e:
messagebox.showerror("输入错误", f"输入无效: {e}")
except Exception as e:
messagebox.showerror("未知错误", f"发送报文时发生未知错误: {e}")
# GUI 界面设计
window = tk.Tk()
window.title("网络通信软件")
hostname, mac, local_ips = get_local_info()
tk.Label(window, text="本机信息:").grid(row=0, column=0)
info_text = tk.Text(window, height=4, width=40)
info_text.grid(row=0, column=1, columnspan=2)
info_text.insert(tk.END, f"主机名: {hostname}\nMAC 地址: {mac}\n可用 IP: {', '.join(local_ips)}")
info_text.config(state=tk.DISABLED)
tk.Label(window, text="本机 IP 地址:").grid(row=1, column=0)
ip_combobox = ttk.Combobox(window, values=local_ips)
ip_combobox.grid(row=1, column=1)
ip_combobox.current(0)
tk.Label(window, text="目标地址:").grid(row=2, column=0)
dest_entry = tk.Entry(window)
dest_entry.grid(row=2, column=1)
tk.Label(window, text="目标端口:").grid(row=3, column=0)
port_entry = tk.Entry(window)
port_entry.grid(row=3, column=1)
tk.Label(window, text="报文类型:").grid(row=4, column=0)
var = tk.StringVar(value="ICMP")
tk.Radiobutton(window, text="ICMP", variable=var, value="ICMP").grid(row=4, column=1)
tk.Radiobutton(window, text="UDP", variable=var, value="UDP").grid(row=4, column=2)
tk.Radiobutton(window, text="TCP", variable=var, value="TCP").grid(row=4, column=3)
result = tk.StringVar()
tk.Label(window, textvariable=result).grid(row=6, column=0, columnspan=3)
tk.Button(window, text="发送报文", command=send_packet).grid(row=5, column=1)
window.mainloop()