From 28298e7952ad6f35135889543e5576cd84aaa92e Mon Sep 17 00:00:00 2001 From: qsc20001102 Date: Fri, 31 Oct 2025 17:10:24 +0800 Subject: [PATCH] 13 --- app.log | 22 ++ .../__pycache__/ping_fun.cpython-313.pyc | Bin 9986 -> 9986 bytes .../__pycache__/telnet_fun.cpython-313.pyc | Bin 0 -> 12020 bytes core/Function/ping_fun.py | 2 +- core/Function/telnet_fun.py | 251 ++++++++++++++++++ core/ui/__pycache__/tab_ping.cpython-313.pyc | Bin 9253 -> 9253 bytes .../ui/__pycache__/tab_telnet.cpython-313.pyc | Bin 0 -> 7116 bytes core/ui/__pycache__/ui_main.cpython-313.pyc | Bin 1576 -> 1714 bytes core/ui/tab_telnet.py | 106 ++++++++ core/ui/ui_main.py | 2 + 10 files changed, 382 insertions(+), 1 deletion(-) create mode 100644 core/Function/__pycache__/telnet_fun.cpython-313.pyc create mode 100644 core/Function/telnet_fun.py create mode 100644 core/ui/__pycache__/tab_telnet.cpython-313.pyc create mode 100644 core/ui/tab_telnet.py diff --git a/app.log b/app.log index ab268c5..6a6373f 100644 --- a/app.log +++ b/app.log @@ -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: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: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 端口连接情况 diff --git a/core/Function/__pycache__/ping_fun.cpython-313.pyc b/core/Function/__pycache__/ping_fun.cpython-313.pyc index 0794e8c81f77f15b6a913845a361255f96dd73d7..61d72c1c2393ccdfb5bdd0ff56dcd752a3a27682 100644 GIT binary patch delta 26 gcmZqjYx3jz%*)Hg00g}G8@bFR8I3kON*?3|09UdGRsaA1 delta 26 gcmZqjYx3jz%*)Hg00e4T8@bFR84Wi(N*?3|09dsKX#fBK diff --git a/core/Function/__pycache__/telnet_fun.cpython-313.pyc b/core/Function/__pycache__/telnet_fun.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..45d2ce1291499131e3fa52a44c786c332c949ca4 GIT binary patch literal 12020 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 literal 0 HcmV?d00001 diff --git a/core/Function/ping_fun.py b/core/Function/ping_fun.py index 4ebb2dc..f35fcb4 100644 --- a/core/Function/ping_fun.py +++ b/core/Function/ping_fun.py @@ -100,7 +100,7 @@ class PingFun: if self.stop_flag: return None - command = ['ping', ip, '-n', '1', '-w', '1000'] + command = ['ping', ip, '-n', '1', '-w', '2000'] if local_ip: command += ['-S', local_ip] try: diff --git a/core/Function/telnet_fun.py b/core/Function/telnet_fun.py new file mode 100644 index 0000000..837bf27 --- /dev/null +++ b/core/Function/telnet_fun.py @@ -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()) diff --git a/core/ui/__pycache__/tab_ping.cpython-313.pyc b/core/ui/__pycache__/tab_ping.cpython-313.pyc index 7a18b90ddd83dbe3eb9bb4f480eb69034e5e9c66..c417260344f2727e8c67b01745264c966885fca1 100644 GIT binary patch delta 18 YcmZ4LvDAa}GcPX}0}wpg$SI=&05t^!*8l(j delta 18 YcmZ4LvDAa}GcPX}0}w3T$SI=&05jACng9R* diff --git a/core/ui/__pycache__/tab_telnet.cpython-313.pyc b/core/ui/__pycache__/tab_telnet.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..eaedbbe53f8b57103536c40b7e4425358cdec6de GIT binary patch literal 7116 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 E05fSXs{jB1 literal 0 HcmV?d00001 diff --git a/core/ui/__pycache__/ui_main.cpython-313.pyc b/core/ui/__pycache__/ui_main.cpython-313.pyc index ed6c54269a5f9c037e42dd9c0f5f365b3bd92ec0..2f1b5a382db35e18c771660efdc4ae1be39c6734 100644 GIT binary patch delta 508 zcmZ3%vx%4YGcPX}0}!Z{v1Hzv$ScX%Hc{QipVgbwi>ruBfgzY7n9-ZNh&zTmNCKn` z6(}*p@R~A0#ESSN7?c=-*`yeP*<;vDnW3Wm>5Q5j6PHijNFWqJ(x6@qJ<}WF{z0Jv+FU1GUR}r1TrC%AxLEM0VcJ{E10wyB`05D ziZ<0`Dg{XgWagzm+uHqN{nS!XAouy|^-ud3KkHomtiS)+oZhEh3xOg)J6TFnD@rtZ zCwnk^G8J)5?qil?6rQ}6Ihu_JsHu3zWImP@%`a>W(n>>sh7v;zw<#k?4Ff|F zj|77fLollpLoizmn<+C?lsBDGlYQbQ>xsYYr9q}8GeIQ4BpZ+fDg3-^a|k0hBU3Q@ z 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') + + + + + + \ No newline at end of file diff --git a/core/ui/ui_main.py b/core/ui/ui_main.py index b57f31d..dae6307 100644 --- a/core/ui/ui_main.py +++ b/core/ui/ui_main.py @@ -3,6 +3,7 @@ from tkinter import ttk, filedialog, messagebox from core.ui.basic_ui import BasicUI from core.ui.tab_ping import PingTab +from core.ui.tab_telnet import TelnetTab import logging logger = logging.getLogger(__name__) @@ -19,6 +20,7 @@ class MainUI(BasicUI): # 实例化各个功能页 self.tabs = { "Ping测试": PingTab(self.tab_control), + "端口扫描服务端": TelnetTab(self.tab_control), } # 添加到 Notebook for name, tab in self.tabs.items():