Python端口扫描器UI实现
下面我将为你的端口扫描工具添加一个Tkinter UI界面,实现所有需求功能。这个实现包括输入区域、扫描控制、结果展示和进度显示。
Python
import tkinter as tk
from tkinter import ttk, messagebox, scrolledtext
import threading
import socket
import queue
from datetime import datetime
class PortScannerApp:
def __init__(self, root):
self.root = root
self.root.title("高级端口扫描器")
self.root.geometry("800x650")
self.root.resizable(True, True)
# 配置样式
self.style = ttk.Style()
self.style.configure("TButton", padding=6, font=('Arial', 10))
self.style.configure("TLabel", font=('Arial', 10))
self.style.configure("TEntry", font=('Arial', 10))
self.style.configure("Header.TLabel", font=('Arial', 11, 'bold'))
# 创建主框架
self.main_frame = ttk.Frame(root, padding=10)
self.main_frame.pack(fill=tk.BOTH, expand=True)
# 输入区域
self.create_input_section()
# 进度区域
self.create_progress_section()
# 结果区域
self.create_results_section()
# 状态栏
self.status_var = tk.StringVar()
self.status_var.set("就绪")
self.status_bar = ttk.Label(root, textvariable=self.status_var, relief=tk.SUNKEN, anchor=tk.W)
self.status_bar.pack(side=tk.BOTTOM, fill=tk.X)
# 扫描控制变量
self.scanning = False
self.scan_thread = None
self.stop_event = threading.Event()
# 结果存储
self.open_ports = []
def create_input_section(self):
"""创建输入参数区域"""
input_frame = ttk.LabelFrame(self.main_frame, text="扫描参数", padding=10)
input_frame.pack(fill=tk.X, pady=(0, 10))
# 目标IP
ttk.Label(input_frame, text="目标IP:", style="Header.TLabel").grid(row=0, column=0, sticky=tk.W, padx=5, pady=5)
self.ip_entry = ttk.Entry(input_frame, width=30)
self.ip_entry.grid(row=0, column=1, padx=5, pady=5)
self.ip_entry.insert(0, "127.0.0.1") # 默认回环地址
# 端口范围
port_frame = ttk.Frame(input_frame)
port_frame.grid(row=1, column=0, columnspan=2, sticky=tk.W, padx=5, pady=5)
ttk.Label(port_frame, text="端口范围:").pack(side=tk.LEFT)
self.start_port_entry = ttk.Entry(port_frame, width=8)
self.start_port_entry.pack(side=tk.LEFT, padx=5)
self.start_port_entry.insert(0, "1")
ttk.Label(port_frame, text="-").pack(side=tk.LEFT)
self.end_port_entry = ttk.Entry(port_frame, width=8)
self.end_port_entry.pack(side=tk.LEFT, padx=5)
self.end_port_entry.insert(0, "1024")
# 线程数
ttk.Label(input_frame, text="线程数:").grid(row=2, column=0, sticky=tk.W, padx=5, pady=5)
self.threads_entry = ttk.Entry(input_frame, width=10)
self.threads_entry.grid(row=2, column=1, sticky=tk.W, padx=5, pady=5)
self.threads_entry.insert(0, "50") # 默认线程数
# 超时时间
ttk.Label(input_frame, text="超时(秒):").grid(row=3, column=0, sticky=tk.W, padx=5, pady=5)
self.timeout_entry = ttk.Entry(input_frame, width=10)
self.timeout_entry.grid(row=3, column=1, sticky=tk.W, padx=5, pady=5)
self.timeout_entry.insert(0, "1.0") # 默认超时时间
# 按钮区域
button_frame = ttk.Frame(input_frame)
button_frame.grid(row=4, column=0, columnspan=2, pady=10)
self.start_btn = ttk.Button(button_frame, text="开始扫描", command=self.start_scan)
self.start_btn.pack(side=tk.LEFT, padx=5)
self.stop_btn = ttk.Button(button_frame, text="停止扫描", command=self.stop_scan, state=tk.DISABLED)
self.stop_btn.pack(side=tk.LEFT, padx=5)
self.clear_btn = ttk.Button(button_frame, text="清除结果", command=self.clear_results)
self.clear_btn.pack(side=tk.LEFT, padx=5)
def create_progress_section(self):
"""创建进度显示区域"""
progress_frame = ttk.LabelFrame(self.main_frame, text="扫描进度", padding=10)
progress_frame.pack(fill=tk.X, pady=(0, 10))
# 进度条
self.progress_bar = ttk.Progressbar(progress_frame, orient=tk.HORIZONTAL, mode='determinate')
self.progress_bar.pack(fill=tk.X, pady=5)
# 进度统计
stats_frame = ttk.Frame(progress_frame)
stats_frame.pack(fill=tk.X, pady=5)
ttk.Label(stats_frame, text="已扫描端口:").pack(side=tk.LEFT, padx=5)
self.scanned_var = tk.StringVar(value="0")
ttk.Label(stats_frame, textvariable=self.scanned_var).pack(side=tk.LEFT, padx=5)
ttk.Label(stats_frame, text="开放端口:").pack(side=tk.LEFT, padx=5)
self.open_var = tk.StringVar(value="0")
ttk.Label(stats_frame, textvariable=self.open_var).pack(side=tk.LEFT, padx=5)
ttk.Label(stats_frame, text="总端口:").pack(side=tk.LEFT, padx=5)
self.total_var = tk.StringVar(value="0")
ttk.Label(stats_frame, textvariable=self.total_var).pack(side=tk.LEFT, padx=5)
ttk.Label(stats_frame, text="进度:").pack(side=tk.LEFT, padx=5)
self.percent_var = tk.StringVar(value="0%")
ttk.Label(stats_frame, textvariable=self.percent_var).pack(side=tk.LEFT, padx=5)
ttk.Label(stats_frame, text="预计剩余时间:").pack(side=tk.LEFT, padx=5)
self.eta_var = tk.StringVar(value="--:--")
ttk.Label(stats_frame, textvariable=self.eta_var).pack(side=tk.LEFT, padx=5)
def create_results_section(self):
"""创建结果展示区域"""
results_frame = ttk.LabelFrame(self.main_frame, text="扫描结果", padding=10)
results_frame.pack(fill=tk.BOTH, expand=True)
# 统计信息
stats_frame = ttk.Frame(results_frame)
stats_frame.pack(fill=tk.X, pady=(0, 5))
ttk.Label(stats_frame, text="开放端口数量:", style="Header.TLabel").pack(side=tk.LEFT, padx=5)
self.count_var = tk.StringVar(value="0")
ttk.Label(stats_frame, textvariable=self.count_var, font=('Arial', 11, 'bold')).pack(side=tk.LEFT, padx=5)
ttk.Label(stats_frame, text="开放端口列表:").pack(side=tk.LEFT, padx=5)
self.ports_var = tk.StringVar(value="")
ttk.Label(stats_frame, textvariable=self.ports_var, font=('Arial', 10)).pack(side=tk.LEFT, padx=5)
# 结果表格
columns = ("端口", "服务", "状态")
self.results_tree = ttk.Treeview(results_frame, columns=columns, show="headings")
# 设置列宽
self.results_tree.column("端口", width=80, anchor=tk.CENTER)
self.results_tree.column("服务", width=200, anchor=tk.CENTER)
self.results_tree.column("状态", width=100, anchor=tk.CENTER)
# 设置列标题
for col in columns:
self.results_tree.heading(col, text=col)
# 添加滚动条
scrollbar = ttk.Scrollbar(results_frame, orient=tk.VERTICAL, command=self.results_tree.yview)
self.results_tree.configure(yscroll=scrollbar.set)
scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
self.results_tree.pack(fill=tk.BOTH, expand=True)
# 日志区域
log_frame = ttk.LabelFrame(results_frame, text="扫描日志", padding=5)
log_frame.pack(fill=tk.X, pady=(10, 0))
self.log_text = scrolledtext.ScrolledText(log_frame, height=5, wrap=tk.WORD)
self.log_text.pack(fill=tk.BOTH, expand=True)
self.log_text.config(state=tk.DISABLED)
def log_message(self, message):
"""添加消息到日志区域"""
timestamp = datetime.now().strftime("%H:%M:%S")
formatted_message = f"[{timestamp}] {message}\n"
self.log_text.config(state=tk.NORMAL)
self.log_text.insert(tk.END, formatted_message)
self.log_text.see(tk.END) # 滚动到底部
self.log_text.config(state=tk.DISABLED)
def update_progress(self, scanned, open_count, total, eta):
"""更新进度显示"""
self.scanned_var.set(str(scanned))
self.open_var.set(str(open_count))
self.total_var.set(str(total))
# 计算并更新进度百分比
if total > 0:
percent = int((scanned / total) * 100)
self.progress_bar['value'] = percent
self.percent_var.set(f"{percent}%")
else:
self.progress_bar['value'] = 0
self.percent_var.set("0%")
# 更新预计剩余时间
self.eta_var.set(eta if eta != float('inf') else "--:--")
def add_open_port(self, port, service):
"""添加开放端口到结果列表"""
self.open_ports.append(port)
self.count_var.set(str(len(self.open_ports)))
self.ports_var.set(", ".join(map(str, sorted(self.open_ports))))
# 添加到表格
self.results_tree.insert("", tk.END, values=(port, service, "开放"))
self.results_tree.yview_moveto(1) # 滚动到底部
def clear_results(self):
"""清除扫描结果"""
self.open_ports = []
self.count_var.set("0")
self.ports_var.set("")
self.results_tree.delete(*self.results_tree.get_children())
self.log_text.config(state=tk.NORMAL)
self.log_text.delete(1.0, tk.END)
self.log_text.config(state=tk.DISABLED)
self.log_message("结果已清除")
def start_scan(self):
"""开始扫描"""
if self.scanning:
return
# 获取输入参数
try:
target_ip = self.ip_entry.get()
start_port = int(self.start_port_entry.get())
end_port = int(self.end_port_entry.get())
num_threads = int(self.threads_entry.get())
timeout = float(self.timeout_entry.get())
if start_port < 1 or end_port > 65535 or start_port > end_port:
raise ValueError("端口范围无效")
if num_threads < 1 or num_threads > 500:
raise ValueError("线程数应在1-500之间")
if timeout < 0.1 or timeout > 10:
raise ValueError("超时时间应在0.1-10秒之间")
except ValueError as e:
messagebox.showerror("输入错误", str(e))
return
# 重置扫描状态
self.stop_event.clear()
self.scanning = True
self.start_btn.config(state=tk.DISABLED)
self.stop_btn.config(state=tk.NORMAL)
self.clear_results()
self.update_progress(0, 0, end_port - start_port + 1, "--:--")
# 创建扫描队列
self.scan_queue = queue.Queue()
for port in range(start_port, end_port + 1):
self.scan_queue.put(port)
# 启动扫描线程
self.scan_thread = threading.Thread(
target=self.run_scan,
args=(target_ip, num_threads, timeout),
daemon=True
)
self.scan_thread.start()
# 启动UI更新线程
self.root.after(100, self.update_ui)
self.log_message(f"开始扫描 {target_ip} 端口 {start_port}-{end_port}")
self.log_message(f"使用 {num_threads} 个线程,超时 {timeout} 秒")
def run_scan(self, target_ip, num_threads, timeout):
"""执行端口扫描(在后台线程中运行)"""
threads = []
self.start_time = datetime.now()
# 创建工作线程
for _ in range(num_threads):
t = threading.Thread(target=self.scan_worker, args=(target_ip, timeout))
t.daemon = True
t.start()
threads.append(t)
# 等待所有线程完成
for t in threads:
t.join()
# 扫描完成
self.scanning = False
duration = (datetime.now() - self.start_time).total_seconds()
self.log_message(f"扫描完成!耗时 {duration:.2f} 秒,发现 {len(self.open_ports)} 个开放端口")
def scan_worker(self, target_ip, timeout):
"""扫描工作线程"""
while not self.scan_queue.empty() and not self.stop_event.is_set():
try:
port = self.scan_queue.get_nowait()
except queue.Empty:
break
try:
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.settimeout(timeout)
result = s.connect_ex((target_ip, port))
if result == 0: # 端口开放
try:
service = socket.getservbyport(port, 'tcp')
except:
service = "未知服务"
self.add_open_port(port, service)
except Exception as e:
# 记录错误但不中断扫描
pass
finally:
self.scan_queue.task_done()
def update_ui(self):
"""更新UI(在主线程中运行)"""
if self.scanning:
# 计算扫描统计
total_ports = int(self.total_var.get())
scanned_ports = total_ports - self.scan_queue.qsize()
open_count = len(self.open_ports)
# 计算预计剩余时间
elapsed = (datetime.now() - self.start_time).total_seconds()
if scanned_ports > 0:
ports_per_sec = scanned_ports / elapsed
remaining_ports = total_ports - scanned_ports
eta_seconds = remaining_ports / ports_per_sec if ports_per_sec > 0 else float('inf')
if eta_seconds < 60:
eta_str = f"{int(eta_seconds)}秒"
elif eta_seconds < 3600:
eta_str = f"{int(eta_seconds/60)}分{int(eta_seconds%60)}秒"
else:
hours = int(eta_seconds // 3600)
minutes = int((eta_seconds % 3600) // 60)
eta_str = f"{hours}小时{minutes}分"
else:
eta_str = "计算中..."
# 更新UI
self.update_progress(scanned_ports, open_count, total_ports, eta_str)
# 每100毫秒再次检查
self.root.after(100, self.update_ui)
else:
# 扫描完成
self.start_btn.config(state=tk.NORMAL)
self.stop_btn.config(state=tk.DISABLED)
self.status_var.set("扫描完成")
def stop_scan(self):
"""停止扫描"""
if self.scanning:
self.stop_event.set()
self.scanning = False
self.start_btn.config(state=tk.NORMAL)
self.stop_btn.config(state=tk.DISABLED)
self.status_var.set("扫描已停止")
self.log_message("扫描已停止")
# 运行应用
if __name__ == "__main__":
root = tk.Tk()
app = PortScannerApp(root)
root.mainloop()
功能说明
这个端口扫描器UI实现了所有你要求的功能:
输入区域:
目标IP地址输入框(默认127.0.0.1)
起始端口和结束端口范围设置
线程数量设置(控制并发扫描数)
超时时间设置(秒)
开始/停止/清除按钮
进度显示:
进度条直观显示扫描进度
实时统计:已扫描端口数、开放端口数、总端口数
进度百分比显示
预计剩余时间计算
结果展示:
开放端口总数显示
开放端口列表(按端口号排序)
表格展示详细结果(端口号、服务类型、状态)
滚动日志区域显示扫描过程中的详细信息
扫描控制:
多线程扫描,不阻塞UI
可随时停止扫描
扫描完成后自动统计结果 基于以上所述进行网络空间安全前沿技术大作业实验报告的理论分析,流程图(类似于思维导图,用word插入里面的插图来表示)显示这两个要求
最新发布