157 lines
5.7 KiB
Python
157 lines
5.7 KiB
Python
import tkinter as tk
|
||
from tkinter import scrolledtext, messagebox
|
||
import subprocess
|
||
import concurrent.futures
|
||
import threading
|
||
import re
|
||
import sys
|
||
|
||
class PingFun:
|
||
def __init__(self, result_box: scrolledtext.ScrolledText):
|
||
self.result_box = result_box
|
||
|
||
# Ping 状态和统计
|
||
self.process = None
|
||
self.stop_flag = False
|
||
self.sent = 0
|
||
self.received = 0
|
||
self.rtts = []
|
||
|
||
def strat_ping(self, host, local_ip=None, callback=None):
|
||
"""开始 ping"""
|
||
self.callback = callback
|
||
self.result_box.delete('1.0', tk.END)
|
||
self.sent = 0
|
||
self.received = 0
|
||
self.rtts.clear()
|
||
self.stop_flag = False
|
||
|
||
command = ['ping', host, '-t']
|
||
if local_ip:
|
||
command += ['-S', local_ip]
|
||
|
||
threading.Thread(target=self.ping, args=(command,), daemon=True).start()
|
||
|
||
def stop_ping(self):
|
||
"""手动停止 ping"""
|
||
if self.process:
|
||
self.process.terminate()
|
||
self.result_box.insert(tk.END, "\nPing 已手动停止。\n")
|
||
self.result_box.see(tk.END)
|
||
self.process = None
|
||
self.show_statistics()
|
||
|
||
def ping(self, command):
|
||
"""执行 ping 命令并处理输出"""
|
||
rtt_pattern = re.compile(r'时间[=<](\d+)ms', re.IGNORECASE)
|
||
try:
|
||
# 👇关键:隐藏 CMD 窗口
|
||
creationflags = subprocess.CREATE_NO_WINDOW if sys.platform.startswith("win") else 0
|
||
|
||
self.process = subprocess.Popen(
|
||
command,
|
||
stdout=subprocess.PIPE,
|
||
stderr=subprocess.STDOUT,
|
||
universal_newlines=True,
|
||
creationflags=creationflags # ✅ 加上这个!
|
||
)
|
||
for line in iter(self.process.stdout.readline, ''):
|
||
if self.stop_flag:
|
||
break
|
||
if line:
|
||
self.result_box.insert(tk.END, line)
|
||
self.result_box.see(tk.END)
|
||
self.sent += 1
|
||
match = rtt_pattern.search(line)
|
||
if match:
|
||
self.received += 1
|
||
self.rtts.append(float(match.group(1)))
|
||
except Exception as e:
|
||
self.result_box.insert(tk.END, f"Ping 失败: {e}\n")
|
||
finally:
|
||
self.process = None
|
||
if self.callback:
|
||
self.callback()
|
||
|
||
def show_statistics(self):
|
||
if self.sent == 0:
|
||
return
|
||
loss = (self.sent - self.received) / self.sent * 100
|
||
stats = f"\n==== Ping 统计 ====\n" \
|
||
f"发送: {self.sent},接收: {self.received},丢包率: {loss:.2f}%\n"
|
||
if self.rtts:
|
||
stats += f"最小延迟: {min(self.rtts)} ms,最大延迟: {max(self.rtts)} ms,平均延迟: {sum(self.rtts)/len(self.rtts):.2f} ms\n"
|
||
self.result_box.insert(tk.END, stats)
|
||
self.result_box.see(tk.END)
|
||
|
||
# ================= 批量 Ping =================
|
||
def start_batch_ping(self, net_prefix, start, end, local_ip=None, callback=None):
|
||
self.callback = callback
|
||
self.stop_flag = False
|
||
self.result_box.delete('1.0', tk.END)
|
||
self.result_box.insert(tk.END, f"开始并发 Ping:{net_prefix}{start} - {net_prefix}{end}\n\n")
|
||
|
||
self.batch_thread = threading.Thread(
|
||
target=self._concurrent_batch_ping, args=(net_prefix, start, end, local_ip), daemon=True)
|
||
self.batch_thread.start()
|
||
|
||
def _ping_one_ip(self, ip, local_ip=None):
|
||
"""Ping 单个 IP 地址并返回结果"""
|
||
if self.stop_flag:
|
||
return None
|
||
|
||
command = ['ping', ip, '-n', '1', '-w', '2000']
|
||
if local_ip:
|
||
command += ['-S', local_ip]
|
||
|
||
try:
|
||
# 👇 同样隐藏 CMD 窗口
|
||
creationflags = subprocess.CREATE_NO_WINDOW if sys.platform.startswith("win") else 0
|
||
|
||
result = subprocess.run(
|
||
command,
|
||
stdout=subprocess.PIPE,
|
||
stderr=subprocess.PIPE,
|
||
text=True,
|
||
timeout=2,
|
||
creationflags=creationflags # ✅ 加上
|
||
)
|
||
|
||
if "TTL=" in result.stdout.upper():
|
||
return f"{ip} ✅ 通\n"
|
||
else:
|
||
return f"{ip} ❌ 不通\n"
|
||
except subprocess.TimeoutExpired:
|
||
return f"{ip} ⚠️ 超时\n"
|
||
except Exception as e:
|
||
return f"{ip} 错误: {e}\n"
|
||
|
||
def _concurrent_batch_ping(self, net_prefix, start, end, local_ip=None):
|
||
ip_list = [f"{net_prefix}{i}" for i in range(start, end + 1)]
|
||
max_workers = min(50, len(ip_list))
|
||
with concurrent.futures.ThreadPoolExecutor(max_workers=max_workers) as executor:
|
||
future_to_ip = {executor.submit(self._ping_one_ip, ip, local_ip): ip for ip in ip_list}
|
||
for future in concurrent.futures.as_completed(future_to_ip):
|
||
if self.stop_flag:
|
||
break
|
||
result = future.result()
|
||
if result:
|
||
self.result_box.insert(tk.END, result)
|
||
self.result_box.see(tk.END)
|
||
|
||
if not self.stop_flag:
|
||
self.result_box.insert(tk.END, "\n并发批量 Ping 完成。\n")
|
||
else:
|
||
self.result_box.insert(tk.END, "\n批量 Ping 已停止。\n")
|
||
self.result_box.see(tk.END)
|
||
if self.callback:
|
||
self.callback()
|
||
|
||
def stop_batch_ping(self):
|
||
if not self.batch_thread or not self.batch_thread.is_alive():
|
||
messagebox.showinfo("提示", "当前没有正在运行的批量 Ping。")
|
||
return
|
||
self.stop_flag = True
|
||
self.result_box.insert(tk.END, "\n正在尝试停止批量 Ping...\n")
|
||
self.result_box.see(tk.END)
|