13
This commit is contained in:
@@ -269,3 +269,25 @@
|
|||||||
2025-10-31 15:57:02,030 [INFO] 开始由192.168.10.1 批量 Ping 192.168.1.1 - 192.168.1.255
|
2025-10-31 15:57:02,030 [INFO] 开始由192.168.10.1 批量 Ping 192.168.1.1 - 192.168.1.255
|
||||||
2025-10-31 15:57:05,860 [INFO] 开始由192.168.10.1 批量 Ping 192.168.1.1 - 192.168.1.255
|
2025-10-31 15:57:05,860 [INFO] 开始由192.168.10.1 批量 Ping 192.168.1.1 - 192.168.1.255
|
||||||
2025-10-31 15:57:06,885 [INFO] 停止由192.168.10.1 批量 Ping 192.168.1.1 - 192.168.1.255
|
2025-10-31 15:57:06,885 [INFO] 停止由192.168.10.1 批量 Ping 192.168.1.1 - 192.168.1.255
|
||||||
|
2025-10-31 15:59:58,363 [INFO] 日志系统初始化完成,日志文件路径:E:\Git\Network-tools\app.log
|
||||||
|
2025-10-31 16:02:26,037 [INFO] 开始由 Ping www.baidu.com
|
||||||
|
2025-10-31 16:02:30,715 [INFO] 停止由 Ping www.baidu.com
|
||||||
|
2025-10-31 16:10:57,005 [INFO] 日志系统初始化完成,日志文件路径:E:\Git\Network-tools\app.log
|
||||||
|
2025-10-31 16:10:58,442 [INFO] 开始批量 Ping 192.168.1.1 - 192.168.1.255
|
||||||
|
2025-10-31 16:13:23,888 [INFO] 日志系统初始化完成,日志文件路径:E:\Git\Network-tools\app.log
|
||||||
|
2025-10-31 16:13:35,360 [INFO] 开始批量 Ping 192.168.1.1 - 192.168.1.255
|
||||||
|
2025-10-31 16:14:22,199 [INFO] 停止批量 Ping 192.168.1.1 - 192.168.1.255
|
||||||
|
2025-10-31 16:26:02,682 [INFO] 日志系统初始化完成,日志文件路径:E:\Git\Network-tools\app.log
|
||||||
|
2025-10-31 16:26:18,252 [INFO] 日志系统初始化完成,日志文件路径:E:\Git\Network-tools\app.log
|
||||||
|
2025-10-31 16:37:44,993 [INFO] 日志系统初始化完成,日志文件路径:E:\Git\Network-tools\app.log
|
||||||
|
2025-10-31 16:38:08,211 [INFO] 日志系统初始化完成,日志文件路径:E:\Git\Network-tools\app.log
|
||||||
|
2025-10-31 16:41:18,553 [INFO] 日志系统初始化完成,日志文件路径:E:\Git\Network-tools\app.log
|
||||||
|
2025-10-31 16:58:02,766 [INFO] 日志系统初始化完成,日志文件路径:E:\Git\Network-tools\app.log
|
||||||
|
2025-10-31 16:58:26,946 [INFO] 日志系统初始化完成,日志文件路径:E:\Git\Network-tools\app.log
|
||||||
|
2025-10-31 17:00:32,403 [INFO] 日志系统初始化完成,日志文件路径:E:\Git\Network-tools\app.log
|
||||||
|
2025-10-31 17:01:12,536 [INFO] 日志系统初始化完成,日志文件路径:E:\Git\Network-tools\app.log
|
||||||
|
2025-10-31 17:01:18,984 [INFO] 开始测试202.89.233.100 的 100 到 1000 端口连接情况
|
||||||
|
2025-10-31 17:01:50,516 [INFO] 开始测试202.89.233.100 的 100 到 1000 端口连接情况
|
||||||
|
2025-10-31 17:03:14,076 [INFO] 开始测试202.89.233.100 的 1 到 1000 端口连接情况
|
||||||
|
2025-10-31 17:09:53,765 [INFO] 开始测试202.89.233.100 的 1 到 1000 端口连接情况
|
||||||
|
2025-10-31 17:09:55,506 [INFO] 开始测试202.89.233.100 的 1 到 1000 端口连接情况
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
@@ -100,7 +100,7 @@ class PingFun:
|
|||||||
if self.stop_flag:
|
if self.stop_flag:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
command = ['ping', ip, '-n', '1', '-w', '1000']
|
command = ['ping', ip, '-n', '1', '-w', '2000']
|
||||||
if local_ip:
|
if local_ip:
|
||||||
command += ['-S', local_ip]
|
command += ['-S', local_ip]
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -0,0 +1,251 @@
|
|||||||
|
import socket
|
||||||
|
import threading
|
||||||
|
import concurrent.futures
|
||||||
|
import tkinter as tk
|
||||||
|
from tkinter import scrolledtext, messagebox
|
||||||
|
from typing import Iterable, Optional, List
|
||||||
|
|
||||||
|
|
||||||
|
class PortScanner:
|
||||||
|
"""
|
||||||
|
PortScanner: 在 Tkinter 的 ScrolledText 中显示端口检测结果的工具类。
|
||||||
|
|
||||||
|
构造:
|
||||||
|
scanner = PortScanner(result_box)
|
||||||
|
|
||||||
|
方法:
|
||||||
|
test_connect(ip, port, timeout=1.0)
|
||||||
|
- 直接测试单个 ip:port 是否可连通(立即返回 bool,并在结果框显示一行结果)
|
||||||
|
|
||||||
|
start_range_scan(ip, start_port, end_port, timeout=1.0, max_workers=100)
|
||||||
|
- 并发扫描端口范围 [start_port, end_port](包含端口边界)
|
||||||
|
- 扫描结果会实时写入 result_box
|
||||||
|
|
||||||
|
start_list_scan(ip, ports: Iterable[int], timeout=1.0, max_workers=100)
|
||||||
|
- 并发扫描给定端口列表
|
||||||
|
|
||||||
|
stop_scan()
|
||||||
|
- 尝试中止正在进行的扫描(设置停止标志,后续任务检测到后会停止提交或返回)
|
||||||
|
|
||||||
|
注意:
|
||||||
|
- 使用 TCP 连接测试(socket.connect),适合服务端口检测。
|
||||||
|
- GUI 写入通过 self._append_text(...) 调度到主线程,保证线程安全。
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, result_box: scrolledtext.ScrolledText):
|
||||||
|
self.result_box = result_box
|
||||||
|
|
||||||
|
# 扫描控制状态
|
||||||
|
self._stop_flag = False # 外部调用 stop_scan() 会把此标志设为 True
|
||||||
|
self._scan_thread: Optional[threading.Thread] = None
|
||||||
|
self._executor: Optional[concurrent.futures.ThreadPoolExecutor] = None
|
||||||
|
|
||||||
|
# 用于统计(可选)
|
||||||
|
self._total = 0
|
||||||
|
self._done = 0
|
||||||
|
self._open_ports: List[int] = []
|
||||||
|
|
||||||
|
# -------------------------
|
||||||
|
# 辅助方法:线程安全地向结果框写文本
|
||||||
|
# -------------------------
|
||||||
|
def _append_text(self, text: str):
|
||||||
|
"""
|
||||||
|
把文本插入到 result_box。因为可能从子线程调用,所以用 after 调度到主线程执行。
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# schedule on main thread immediately
|
||||||
|
self.result_box.after(0, lambda: (self.result_box.insert(tk.END, text), self.result_box.see(tk.END)))
|
||||||
|
except Exception:
|
||||||
|
# 在极少数情况下(例如 result_box 已销毁),捕获异常避免崩溃
|
||||||
|
pass
|
||||||
|
|
||||||
|
# -------------------------
|
||||||
|
# 单端口测试
|
||||||
|
# -------------------------
|
||||||
|
def test_connect(self, ip: str, port: int, timeout: float = 1.0) -> bool:
|
||||||
|
"""
|
||||||
|
立即测试单个 ip:port 是否可以 TCP 连接。
|
||||||
|
- 返回 True(可连通)或 False(不可连通)
|
||||||
|
- 同时把结果写入 result_box(通过主线程调度)
|
||||||
|
"""
|
||||||
|
addr = (ip, int(port))
|
||||||
|
status = False
|
||||||
|
try:
|
||||||
|
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
|
sock.settimeout(timeout)
|
||||||
|
sock.connect(addr)
|
||||||
|
status = True
|
||||||
|
sock.close()
|
||||||
|
except Exception:
|
||||||
|
status = False
|
||||||
|
|
||||||
|
# 输出结果(在 GUI 中显示)
|
||||||
|
self._append_text(f"{ip}:{port} {'✅ 开放' if status else '❌ 关闭/不可达'}\n")
|
||||||
|
return status
|
||||||
|
|
||||||
|
# -------------------------
|
||||||
|
# 并发单端口任务(内部使用)
|
||||||
|
# -------------------------
|
||||||
|
def _scan_single_port(self, ip: str, port: int, timeout: float) -> str:
|
||||||
|
"""
|
||||||
|
线程池中运行的单个端口检测任务,返回一行结果字符串。
|
||||||
|
任务必须尽量短小(快速返回),并在开始前检查 stop_flag。
|
||||||
|
"""
|
||||||
|
if self._stop_flag:
|
||||||
|
return "" # 为空表示不输出
|
||||||
|
|
||||||
|
try:
|
||||||
|
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
|
sock.settimeout(timeout)
|
||||||
|
sock.connect((ip, port))
|
||||||
|
sock.close()
|
||||||
|
result = f"{ip}:{port} ✅ 开放\n"
|
||||||
|
# 记录到本地开放端口列表(线程安全地追加)
|
||||||
|
self._open_ports.append(port)
|
||||||
|
except Exception:
|
||||||
|
result = f"{ip}:{port} ❌ 关闭/不可达\n"
|
||||||
|
|
||||||
|
# 更新进度计数(最好在主线程更新显示,这里在子线程更新计数)
|
||||||
|
self._done += 1
|
||||||
|
return result
|
||||||
|
|
||||||
|
# -------------------------
|
||||||
|
# 并发扫描(范围或列表)
|
||||||
|
# -------------------------
|
||||||
|
def start_range_scan(self, ip: str, start_port: int, end_port: int, timeout: float = 0.8, max_workers: int = 200):
|
||||||
|
"""
|
||||||
|
并发扫描端口范围 [start_port, end_port](含两端)。
|
||||||
|
结果实时写入 result_box。单次扫描在后台线程中运行(不会阻塞主线程)。
|
||||||
|
"""
|
||||||
|
# 参数校验(基本)
|
||||||
|
try:
|
||||||
|
start_port = int(start_port); end_port = int(end_port)
|
||||||
|
except Exception:
|
||||||
|
messagebox.showwarning("输入错误", "起始端口和结束端口必须为整数")
|
||||||
|
return
|
||||||
|
if start_port < 1 or end_port > 65535 or start_port > end_port:
|
||||||
|
messagebox.showwarning("输入错误", "端口范围不合法(1-65535 且 起始<=结束)")
|
||||||
|
return
|
||||||
|
|
||||||
|
ports = list(range(start_port, end_port + 1))
|
||||||
|
self.start_list_scan(ip, ports, timeout=timeout, max_workers=max_workers)
|
||||||
|
|
||||||
|
def start_list_scan(self, ip: str, ports: Iterable[int], timeout: float = 0.8, max_workers: int = 200):
|
||||||
|
"""
|
||||||
|
并发扫描指定的端口列表。
|
||||||
|
- ip: 目标 IP(字符串)
|
||||||
|
- ports: 可迭代的端口集合(如 list、range 等)
|
||||||
|
- timeout: 单端口连接超时(秒)
|
||||||
|
- max_workers: 最大并发数(线程池大小)
|
||||||
|
"""
|
||||||
|
# 防止重复启动
|
||||||
|
if self._scan_thread and self._scan_thread.is_alive():
|
||||||
|
messagebox.showinfo("提示", "已有扫描任务在运行,请先停止后再启动新的扫描。")
|
||||||
|
return
|
||||||
|
|
||||||
|
# 将 ports 转为列表并进行基本校验
|
||||||
|
try:
|
||||||
|
ports_list = [int(p) for p in ports]
|
||||||
|
except Exception:
|
||||||
|
messagebox.showwarning("输入错误", "端口列表包含非法值")
|
||||||
|
return
|
||||||
|
if not ports_list:
|
||||||
|
messagebox.showwarning("输入错误", "端口列表为空")
|
||||||
|
return
|
||||||
|
for p in ports_list:
|
||||||
|
if p < 1 or p > 65535:
|
||||||
|
messagebox.showwarning("输入错误", f"端口 {p} 不在合法范围 1-65535")
|
||||||
|
return
|
||||||
|
|
||||||
|
# 重置状态
|
||||||
|
self._stop_flag = False
|
||||||
|
self._open_ports = []
|
||||||
|
self._total = len(ports_list)
|
||||||
|
self._done = 0
|
||||||
|
|
||||||
|
# 清空 result_box 并输出起始信息(主线程调度)
|
||||||
|
self._append_text(f"开始并发端口扫描:目标 {ip},共 {self._total} 个端口\n")
|
||||||
|
|
||||||
|
# 后台线程用于管理线程池与结果收集,确保 GUI 不阻塞
|
||||||
|
def manager():
|
||||||
|
# 根据任务数自适应限制并发数
|
||||||
|
actual_workers = max(1, min(max_workers, self._total))
|
||||||
|
self._executor = concurrent.futures.ThreadPoolExecutor(max_workers=actual_workers)
|
||||||
|
|
||||||
|
# 提交任务
|
||||||
|
futures = {self._executor.submit(self._scan_single_port, ip, port, timeout): port for port in ports_list}
|
||||||
|
|
||||||
|
try:
|
||||||
|
# as_completed 会在每个 future 完成时迭代返回
|
||||||
|
for fut in concurrent.futures.as_completed(futures):
|
||||||
|
if self._stop_flag:
|
||||||
|
# 如果外部发出停止信号,尽量取消未开始的 future(cancel 返回 True 表示取消成功)
|
||||||
|
# 线程池中的任务可能已在运行, cancel 只能取消未开始的任务
|
||||||
|
break
|
||||||
|
|
||||||
|
try:
|
||||||
|
line = fut.result()
|
||||||
|
except Exception as e:
|
||||||
|
line = f"{ip}:{futures.get(fut)} 错误: {e}\n"
|
||||||
|
|
||||||
|
if line:
|
||||||
|
# 把结果写回 GUI(通过 _append_text 安全调度)
|
||||||
|
self._append_text(line)
|
||||||
|
|
||||||
|
# 可选:显示进度(例如:done/total)
|
||||||
|
self._append_text(f"进度: {self._done}/{self._total}\n")
|
||||||
|
|
||||||
|
# 如果 stop_flag 已设,尝试取消尚未开始的任务
|
||||||
|
if self._stop_flag:
|
||||||
|
for f in futures:
|
||||||
|
f.cancel()
|
||||||
|
|
||||||
|
finally:
|
||||||
|
# 关闭线程池
|
||||||
|
if self._executor:
|
||||||
|
self._executor.shutdown(wait=False)
|
||||||
|
self._executor = None
|
||||||
|
|
||||||
|
# 最终输出总结信息(开放端口列表)
|
||||||
|
if not self._stop_flag:
|
||||||
|
self._append_text("\n端口扫描完成。\n")
|
||||||
|
else:
|
||||||
|
self._append_text("\n端口扫描已停止。\n")
|
||||||
|
|
||||||
|
if self._open_ports:
|
||||||
|
self._append_text(f"开放端口: {sorted(self._open_ports)}\n")
|
||||||
|
else:
|
||||||
|
self._append_text("未发现开放端口。\n")
|
||||||
|
|
||||||
|
# 启动后台管理线程
|
||||||
|
self._scan_thread = threading.Thread(target=manager, daemon=True)
|
||||||
|
self._scan_thread.start()
|
||||||
|
|
||||||
|
# -------------------------
|
||||||
|
# 停止扫描
|
||||||
|
# -------------------------
|
||||||
|
def stop_scan(self):
|
||||||
|
"""
|
||||||
|
请求停止正在进行的扫描:设置停止标志并尝试关闭线程池。
|
||||||
|
- 已提交并正在运行的 socket.connect 调用无法被立即中断(但后续任务会被取消)
|
||||||
|
- stop_scan 尽快返回,实际终止需要等待正在运行的任务完成或超时
|
||||||
|
"""
|
||||||
|
if not (self._scan_thread and self._scan_thread.is_alive()):
|
||||||
|
messagebox.showinfo("提示", "当前没有正在运行的扫描任务。")
|
||||||
|
return
|
||||||
|
|
||||||
|
self._append_text("\n正在停止扫描,请稍候...\n")
|
||||||
|
self._stop_flag = True
|
||||||
|
|
||||||
|
# 尝试立即关闭线程池(不等待正在运行任务)
|
||||||
|
if self._executor:
|
||||||
|
try:
|
||||||
|
self._executor.shutdown(wait=False)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# -------------------------
|
||||||
|
# 可选:返回当前扫描状态(供外部查询)
|
||||||
|
# -------------------------
|
||||||
|
def is_scanning(self) -> bool:
|
||||||
|
return bool(self._scan_thread and self._scan_thread.is_alive())
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,106 @@
|
|||||||
|
import tkinter as tk
|
||||||
|
from tkinter import ttk, filedialog, messagebox, scrolledtext
|
||||||
|
|
||||||
|
from core.ui.basic_ui import BasicUI
|
||||||
|
from core.Function.telnet_fun import PortScanner
|
||||||
|
|
||||||
|
import logging
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
class TelnetTab(ttk.Frame, BasicUI):
|
||||||
|
def __init__(self, parent):
|
||||||
|
super().__init__(parent)
|
||||||
|
self.telnet_ui()
|
||||||
|
self.telnet_fun = PortScanner(self.result_box)
|
||||||
|
|
||||||
|
def telnet_ui(self):
|
||||||
|
"""端口扫描界面布局"""
|
||||||
|
self.create_assigntelnet_section()
|
||||||
|
self.create_batchtelnet_section()
|
||||||
|
self.create_outputping_section()
|
||||||
|
# --------------------------------------UI界面布局函数--------------------------------------
|
||||||
|
def create_assigntelnet_section(self):
|
||||||
|
# 区域标签
|
||||||
|
frame = ttk.LabelFrame(self, text="指定端口连接测试")
|
||||||
|
frame.pack(side='top', fill='x', padx=10, pady=5)
|
||||||
|
|
||||||
|
#self.entry_assignIP_A = self.add_input(frame, "本地IP", row=0, col=0)
|
||||||
|
self.assignTelnet_IP = self.add_input(frame, "目标IP", row=0, col=0, inivar="202.89.233.100")
|
||||||
|
self.assignTelnet_port = self.add_input(frame, "目标端口", row=0, col=1, inivar="443", entry_width=10)
|
||||||
|
self.assignTelnet_test = self.add_button(frame, "测试", row=0, col=2, command=self.btn_assignTelnet_test)
|
||||||
|
|
||||||
|
def create_batchtelnet_section(self):
|
||||||
|
# 区域标签
|
||||||
|
frame = ttk.LabelFrame(self, text="批量端口连接测试")
|
||||||
|
frame.pack(side='top', fill='x', padx=10, pady=5)
|
||||||
|
|
||||||
|
self.batchTelnet_IP = self.add_input(frame, "目标IP", row=0, col=0, inivar="202.89.233.100")
|
||||||
|
self.batchTelnet_port_begin = self.add_input(frame, "起始端口", row=0, col=1, inivar="100", entry_width=10)
|
||||||
|
self.batchTelnet_port_end = self.add_input(frame, "结束端口", row=0, col=2, inivar="1000", entry_width=10)
|
||||||
|
self.batchTelnet_start = self.add_button(frame, "开始", row=0, col=3, command=self.btn_batchTelnet_start)
|
||||||
|
self.batchTelnet_stop = self.add_button(frame, "停止", row=0, col=4, command=self.btn_batchTelnet_stop)
|
||||||
|
|
||||||
|
def create_outputping_section(self):
|
||||||
|
# 区域标签
|
||||||
|
frame = ttk.LabelFrame(self, text="端口扫描结果输出")
|
||||||
|
frame.pack(side='top', fill='x', padx=10, pady=5)
|
||||||
|
|
||||||
|
self.result_box = scrolledtext.ScrolledText(frame, width=100, height=20)
|
||||||
|
self.result_box.pack(pady=10)
|
||||||
|
# --------------------------------------按钮回调函数--------------------------------------
|
||||||
|
def btn_assignTelnet_test(self):
|
||||||
|
self.IP = self.assignTelnet_IP['var'].get()
|
||||||
|
self.port = self.assignTelnet_port['var'].get()
|
||||||
|
if not self.IP:
|
||||||
|
messagebox.showwarning("输入错误", "请输入目标IP或域名!")
|
||||||
|
return
|
||||||
|
if not self.port:
|
||||||
|
messagebox.showwarning("输入错误", "请输入目标端口!")
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
port = int(self.port)
|
||||||
|
except ValueError:
|
||||||
|
messagebox.showwarning("输入错误", "端口必须是整数!")
|
||||||
|
return
|
||||||
|
if port < 1 or port > 65535:
|
||||||
|
messagebox.showwarning("输入错误", "起始端口最小为1,结束端口最大为65535!")
|
||||||
|
return
|
||||||
|
logger.info(f"测试{self.IP} 的 {port} 端口连接情况")
|
||||||
|
self.telnet_fun.test_connect(self.IP, port)
|
||||||
|
|
||||||
|
def btn_batchTelnet_start(self):
|
||||||
|
self.IP = self.batchTelnet_IP['var'].get()
|
||||||
|
self.port_begin = self.batchTelnet_port_begin['var'].get()
|
||||||
|
self.port_end = self.batchTelnet_port_end['var'].get()
|
||||||
|
if not self.IP:
|
||||||
|
messagebox.showwarning("输入错误", "请输入目标IP或域名!")
|
||||||
|
return
|
||||||
|
if not self.port_begin or not self.port_end:
|
||||||
|
messagebox.showwarning("输入错误", "请输入目标端口!")
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
port_begin = int(self.port_begin)
|
||||||
|
port_end = int(self.port_end)
|
||||||
|
except ValueError:
|
||||||
|
messagebox.showwarning("输入错误", "端口必须是整数!")
|
||||||
|
return
|
||||||
|
if port_begin < 1 or port_end > 65535:
|
||||||
|
messagebox.showwarning("输入错误", "起始端口最小为1,结束端口最大为65535!")
|
||||||
|
return
|
||||||
|
logger.info(f"开始测试{self.IP} 的 {port_begin} 到 {port_end} 端口连接情况")
|
||||||
|
self.batchTelnet_start['btn'].config(state='disabled')
|
||||||
|
self.telnet_fun.start_range_scan(self.IP, port_begin, port_end)
|
||||||
|
|
||||||
|
def btn_batchTelnet_stop(self):
|
||||||
|
logger.info(f"停止测试{self.IP} 的端口连接情况")
|
||||||
|
self.telnet_fun.stop_scan()
|
||||||
|
self.batchTelnet_start['btn'].config(state='normal')
|
||||||
|
|
||||||
|
def batchTelnet_callback(self):
|
||||||
|
self.batchTelnet_start['btn'].config(state='normal')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -3,6 +3,7 @@ from tkinter import ttk, filedialog, messagebox
|
|||||||
|
|
||||||
from core.ui.basic_ui import BasicUI
|
from core.ui.basic_ui import BasicUI
|
||||||
from core.ui.tab_ping import PingTab
|
from core.ui.tab_ping import PingTab
|
||||||
|
from core.ui.tab_telnet import TelnetTab
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@@ -19,6 +20,7 @@ class MainUI(BasicUI):
|
|||||||
# 实例化各个功能页
|
# 实例化各个功能页
|
||||||
self.tabs = {
|
self.tabs = {
|
||||||
"Ping测试": PingTab(self.tab_control),
|
"Ping测试": PingTab(self.tab_control),
|
||||||
|
"端口扫描服务端": TelnetTab(self.tab_control),
|
||||||
}
|
}
|
||||||
# 添加到 Notebook
|
# 添加到 Notebook
|
||||||
for name, tab in self.tabs.items():
|
for name, tab in self.tabs.items():
|
||||||
|
|||||||
Reference in New Issue
Block a user