import tkinter as tk from tkinter import ttk, messagebox, scrolledtext, filedialog import threading import subprocess import os import sys import time import random import subprocess import qrcode from PIL import Image, ImageTk import io from subprocess import CREATE_NO_WINDOW, STARTUPINFO, STARTF_USESHOWWINDOW import socket import json class CloudHeartbeatManager: """网盘加速心跳管理器(基于现有服务)""" def __init__(self, output_callback, interval=30): self.interval = interval # 心跳间隔(秒) self.is_running = False self.heartbeat_thread = None self.output_callback = output_callback self.heartbeat_count = 0 def start(self): """启动心跳""" if not self.is_running: self.is_running = True self.heartbeat_count = 0 self.heartbeat_thread = threading.Thread(target=self._heartbeat_loop, daemon=True) self.heartbeat_thread.start() self.output_callback("[心跳] 开始发送服务心跳...\n") def stop(self): """停止心跳""" if self.is_running: self.is_running = False if self.heartbeat_thread and self.heartbeat_thread.is_alive(): self.heartbeat_thread.join(timeout=2) self.output_callback(f"[心跳] 停止服务心跳,共发送 {self.heartbeat_count} 次心跳\n") def _heartbeat_loop(self): """心跳循环""" while self.is_running: try: self._send_heartbeat() self.heartbeat_count += 1 # 等待下一次心跳 for i in range(self.interval): if not self.is_running: break time.sleep(1) except Exception as e: self.output_callback(f"[心跳] 发送错误: {e}\n") time.sleep(10) # 出错后等待10秒再重试 def _send_heartbeat(self): """发送心跳包""" try: # 获取本机信息 current_time = time.strftime("%Y-%m-%d %H:%M:%S") # 构造心跳数据 heartbeat_data = { "type": "cloud_accel_heartbeat", "timestamp": time.time(), "time_str": current_time, "service": "kodcloud_accel", "status": "running", "local_ip": "127.0.0.1", "port": 8088, "count": self.heartbeat_count } # 通过HTTP发送心跳到本地服务 self._send_http_heartbeat("127.0.0.1", heartbeat_data) # 显示心跳信息 if self.heartbeat_count % 5 == 0: # 每5次显示一次详细信息 status_msg = f"[心跳] #{self.heartbeat_count} 服务活跃 - 时间: {current_time}\n" else: status_msg = f"[心跳] #{self.heartbeat_count} 服务心跳\n" self.output_callback(status_msg) except Exception as e: self.output_callback(f"[心跳] 发送心跳包失败: {e}\n") def _send_http_heartbeat(self, local_ip, data): """通过HTTP发送心跳到本地服务""" try: import urllib.request import json # 发送HTTP请求到本地服务 url = f"http://{local_ip}:8088" headers = { 'User-Agent': 'CloudAccel-Heartbeat/1.0', 'Content-Type': 'application/json', 'X-Heartbeat': 'true' } # 创建请求 req = urllib.request.Request( url, data=json.dumps(data).encode('utf-8'), headers=headers, method='POST' ) # 发送请求(设置较短超时时间) response = urllib.request.urlopen(req, timeout=5) response.read() # 读取响应 except Exception as e: # 其他异常忽略 pass class FileTransferApp: def __init__(self, root): self.root = root self.root.title("蜗牛创造的高速文件传输") self.root.geometry("680x860") self.root.configure(bg='white') # 设置窗口背景为白色 # 初始化各页面状态 self.page_states = { 'welcome': {}, 'send': { 'is_running': False, 'frpc_process': None, 'p2p_process': None, 'key_entry_text': '', 'file_path': '', 'output_content': '' }, 'receive': { 'is_running': False, 'frpc_process': None, 'p2p_process': None, 'key_entry_text': '', 'save_path': '.\\received_files', 'output_content': '' }, 'cloud': { 'is_running': False, 'cloud_process': None, 'output_content': '' } } self.current_page = None # 网盘加速心跳管理器 self.cloud_heartbeat_manager = None # 设置样式 self.setup_styles() # 创建主框架容器 self.main_frame = tk.Frame(self.root, bg='white') self.main_frame.pack(fill='both', expand=True, padx=20, pady=20) # 显示欢迎界面 self.show_welcome_screen() def save_current_page_state(self): """保存当前页面的状态""" if self.current_page == 'send' and hasattr(self, 'send_output'): self.page_states['send']['key_entry_text'] = self.send_key_entry.get() self.page_states['send']['file_path'] = self.file_path_var.get() self.page_states['send']['output_content'] = self.send_output.get(1.0, tk.END) elif self.current_page == 'receive' and hasattr(self, 'receive_output'): self.page_states['receive']['key_entry_text'] = self.receive_key_entry.get() self.page_states['receive']['save_path'] = self.receive_path_var.get() self.page_states['receive']['output_content'] = self.receive_output.get(1.0, tk.END) elif self.current_page == 'cloud' and hasattr(self, 'cloud_output'): self.page_states['cloud']['output_content'] = self.cloud_output.get(1.0, tk.END) def restore_page_state(self, page_name): """恢复指定页面的状态""" state = self.page_states[page_name] if page_name == 'send' and hasattr(self, 'send_key_entry'): self.send_key_entry.delete(0, tk.END) self.send_key_entry.insert(0, state['key_entry_text']) self.file_path_var.set(state['file_path']) if hasattr(self, 'send_output'): self.send_output.config(state='normal') self.send_output.delete(1.0, tk.END) self.send_output.insert(1.0, state['output_content']) self.send_output.config(state='disabled') # 恢复按钮状态 if state['is_running']: self.send_connect_button.config(state='disabled') self.send_stop_button.config(state='normal') self.send_file_button.config(state='normal' if state['file_path'] else 'disabled') else: self.send_connect_button.config(state='normal') self.send_stop_button.config(state='disabled') self.send_file_button.config(state='disabled') self.update_send_status("运行中" if state['is_running'] else "等待连接") elif page_name == 'receive' and hasattr(self, 'receive_key_entry'): self.receive_key_entry.delete(0, tk.END) self.receive_key_entry.insert(0, state['key_entry_text']) self.receive_path_var.set(state['save_path']) if hasattr(self, 'receive_output'): self.receive_output.config(state='normal') self.receive_output.delete(1.0, tk.END) self.receive_output.insert(1.0, state['output_content']) self.receive_output.config(state='disabled') # 恢复按钮状态 if state['is_running']: self.connect_button.config(state='disabled') self.stop_button.config(state='normal') else: self.connect_button.config(state='normal') self.stop_button.config(state='disabled') self.update_status("运行中" if state['is_running'] else "等待连接") elif page_name == 'cloud' and hasattr(self, 'cloud_output'): self.cloud_output.config(state='normal') self.cloud_output.delete(1.0, tk.END) self.cloud_output.insert(1.0, state['output_content']) self.cloud_output.config(state='disabled') # 恢复按钮状态 if state['is_running']: self.cloud_start_button.config(state='disabled') self.cloud_stop_button.config(state='normal') self.cloud_status_label.config(text="📊 状态: 加速服务运行中", fg="#27ae60") else: self.cloud_start_button.config(state='normal') self.cloud_stop_button.config(state='disabled') self.cloud_status_label.config(text="📊 状态: 等待启动加速服务", fg="#7f8c8d") def setup_styles(self): """设置界面样式""" style = ttk.Style() style.configure('TButton', font=('Microsoft YaHei', 10)) style.configure('Primary.TButton', background='#3498db', foreground='white') style.configure('Success.TButton', background='#2ecc71', foreground='white') style.configure('Danger.TButton', background='#e74c3c', foreground='white') style.configure('Secondary.TButton', background='#95a5a6', foreground='white') def clear_frame(self): """清除当前框架中的所有控件""" for widget in self.main_frame.winfo_children(): widget.destroy() def show_confirm_dialog(self, stop_command, success_callback): """显示确认对话框""" def on_confirm(): # 执行停止命令 stop_command() # 关闭对话框 confirm_dialog.destroy() # 执行成功回调 success_callback() def on_cancel(): confirm_dialog.destroy() # 创建确认对话框 confirm_dialog = tk.Toplevel(self.root) confirm_dialog.title("确认离开") confirm_dialog.geometry("400x200") confirm_dialog.configure(bg='white') confirm_dialog.resizable(False, False) confirm_dialog.transient(self.root) # 设置为主窗口的临时窗口 confirm_dialog.grab_set() # 模态对话框 # 居中显示 confirm_dialog.update_idletasks() x = (self.root.winfo_screenwidth() - confirm_dialog.winfo_width()) // 2 y = (self.root.winfo_screenheight() - confirm_dialog.winfo_height()) // 2 confirm_dialog.geometry(f"+{x}+{y}") # 图标和提示文字 icon_label = tk.Label(confirm_dialog, text="⚠️", font=("Arial", 24), bg='white') icon_label.pack(pady=(20, 10)) message_label = tk.Label( confirm_dialog, text="你正离开当前界面,会停止运行当前功能", font=("Microsoft YaHei", 12), fg="#2c3e50", bg='white', wraplength=350 ) message_label.pack(pady=(0, 20)) # 按钮框架 button_frame = tk.Frame(confirm_dialog, bg='white') button_frame.pack(pady=10) # 取消按钮 cancel_button = tk.Button( button_frame, text="取消", command=on_cancel, font=("Microsoft YaHei", 11), bg="#95a5a6", fg="white", width=10, relief='flat' ) cancel_button.pack(side='left', padx=10) # 确认按钮 confirm_button = tk.Button( button_frame, text="确认", command=on_confirm, font=("Microsoft YaHei", 11), bg="#e74c3c", fg="white", width=10, relief='flat' ) confirm_button.pack(side='left', padx=10) # 绑定回车和ESC键 confirm_dialog.bind('', lambda e: on_confirm()) confirm_dialog.bind('', lambda e: on_cancel()) confirm_button.focus_set() def show_welcome_screen(self): """显示欢迎界面""" if self.current_page: # 根据当前页面类型决定停止命令 if self.current_page == 'send' and self.page_states['send']['is_running']: self.show_confirm_dialog(self.stop_send_connection, self._show_welcome_screen) return elif self.current_page == 'receive' and self.page_states['receive']['is_running']: self.show_confirm_dialog(self.stop_frpc_connection, self._show_welcome_screen) return elif self.current_page == 'cloud' and self.page_states['cloud']['is_running']: self.show_confirm_dialog(self.stop_cloud_accel, self._show_welcome_screen) return # 如果没有运行中的功能,直接显示欢迎界面 self._show_welcome_screen() def _show_welcome_screen(self): """显示欢迎界面""" if self.current_page: self.save_current_page_state() self.clear_frame() self.current_page = 'welcome' # 顶部logo区域 logo_frame = tk.Frame(self.main_frame, bg='white') logo_frame.pack(pady=(30, 20)) # 欢迎标签 welcome_label = tk.Label( logo_frame, text="🐌 欢迎来到蜗牛创造的高速世界", font=("Microsoft YaHei", 20, "bold"), fg="#2c3e50", bg='white' ) welcome_label.pack() # 副标题 subtitle_label = tk.Label( logo_frame, text="安全、高速的文件传输解决方案", font=("Microsoft YaHei", 12), fg="#7f8c8d", bg='white' ) subtitle_label.pack(pady=(5, 30)) # 功能卡片框架 card_frame = tk.Frame(self.main_frame, bg='white') card_frame.pack(pady=20) # 发送卡片 send_card = tk.Frame(card_frame, bg='#f8f9fa', relief='raised', bd=1, padx=20, pady=20) send_card.pack(side='left', padx=15) send_icon = tk.Label(send_card, text="📤", font=("Arial", 24), bg='#f8f9fa') send_icon.pack() send_title = tk.Label(send_card, text="发送文件", font=("Microsoft YaHei", 14, "bold"), fg="#2c3e50", bg='#f8f9fa') send_title.pack(pady=(10, 5)) send_desc = tk.Label(send_card, text="快速安全地发送文件", font=("Microsoft YaHei", 9), fg="#7f8c8d", bg='#f8f9fa') send_desc.pack(pady=(0, 15)) send_button = tk.Button( send_card, text="开始发送", command=self.show_send_screen, font=("Microsoft YaHei", 11, "bold"), bg="#3498db", fg="white", width=12, height=2, relief='flat', cursor='hand2' ) send_button.pack() # 接收卡片 receive_card = tk.Frame(card_frame, bg='#f8f9fa', relief='raised', bd=1, padx=20, pady=20) receive_card.pack(side='left', padx=15) receive_icon = tk.Label(receive_card, text="📥", font=("Arial", 24), bg='#f8f9fa') receive_icon.pack() receive_title = tk.Label(receive_card, text="接收文件", font=("Microsoft YaHei", 14, "bold"), fg="#2c3e50", bg='#f8f9fa') receive_title.pack(pady=(10, 5)) receive_desc = tk.Label(receive_card, text="安全可靠地接收文件", font=("Microsoft YaHei", 9), fg="#7f8c8d", bg='#f8f9fa') receive_desc.pack(pady=(0, 15)) receive_button = tk.Button( receive_card, text="开始接收", command=self.show_receive_screen, font=("Microsoft YaHei", 11, "bold"), bg="#2ecc71", fg="white", width=12, height=2, relief='flat', cursor='hand2' ) receive_button.pack() # 网盘加速卡片 cloud_card = tk.Frame(card_frame, bg='#f8f9fa', relief='raised', bd=1, padx=20, pady=20) cloud_card.pack(side='left', padx=15) cloud_icon = tk.Label(cloud_card, text="⚡", font=("Arial", 24), bg='#f8f9fa') cloud_icon.pack() cloud_title = tk.Label(cloud_card, text="网盘加速", font=("Microsoft YaHei", 14, "bold"), fg="#2c3e50", bg='#f8f9fa') cloud_title.pack(pady=(10, 5)) cloud_desc = tk.Label(cloud_card, text="加速您的网盘访问", font=("Microsoft YaHei", 9), fg="#7f8c8d", bg='#f8f9fa') cloud_desc.pack(pady=(0, 15)) cloud_button = tk.Button( cloud_card, text="加速服务", command=self.show_cloud_accel_screen, font=("Microsoft YaHei", 11, "bold"), bg="#f39c12", fg="white", width=12, height=2, relief='flat', cursor='hand2' ) cloud_button.pack() # 提示语框架 tip_frame = tk.Frame(self.main_frame, bg='white') tip_frame.pack(pady=(10, 30)) tip_label = tk.Label( tip_frame, text="💡 提示:可通过多次打开软件,分别进行发送、接收和加速操作", font=("Microsoft YaHei", 11, "bold"), fg="#e67e22", # 使用橙色突出显示 bg='white', wraplength=600 # 限制宽度,自动换行 ) tip_label.pack() # 底部信息 footer_frame = tk.Frame(self.main_frame, bg='white') footer_frame.pack(side='bottom', pady=20) footer_label = tk.Label( footer_frame, text="© 2025 蜗牛创造 - 让文件传输更简单", font=("Microsoft YaHei", 12), fg="#bdc3c7", bg='white' ) footer_label.pack() def show_cloud_accel_screen(self): """显示网盘加速界面""" if self.current_page and self.current_page != 'cloud': # 检查当前页面是否有运行中的功能 if self.current_page == 'send' and self.page_states['send']['is_running']: self.show_confirm_dialog(self.stop_send_connection, self._show_cloud_accel_screen) return elif self.current_page == 'receive' and self.page_states['receive']['is_running']: self.show_confirm_dialog(self.stop_frpc_connection, self._show_cloud_accel_screen) return # 如果没有运行中的功能,直接显示网盘加速界面 self._show_cloud_accel_screen() def _show_cloud_accel_screen(self): """显示网盘加速界面""" if self.current_page: self.save_current_page_state() self.clear_frame() self.current_page = 'cloud' # 标题栏 title_frame = tk.Frame(self.main_frame, bg='white') title_frame.pack(fill='x', pady=(10, 20)) back_button = self.create_styled_button(title_frame, "← 返回", self.show_welcome_screen, "#95a5a6", 8) back_button.pack(side='left') title_label = tk.Label( title_frame, text="⚡ 网盘加速服务", font=("Microsoft YaHei", 16, "bold"), fg="#2c3e50", bg='white' ) title_label.pack(side='left', padx=10) # 说明文本 desc_frame = tk.Frame(self.main_frame, bg='white') desc_frame.pack(fill='x', pady=(0, 20)) # 获取本机IP地址 local_ip = self.get_local_ip() qr_url = f"http://{local_ip}:8088" desc_text = f"""功能说明: • 为KodCloud等网盘提供加速服务 • 使用XTCP协议建立高速通道 • 电脑服务地址: http://127.0.0.1:8089 • 手机服务地址: {qr_url}""" desc_label = tk.Label( desc_frame, text=desc_text, font=("Microsoft YaHei", 10), fg="#7f8c8d", bg='white', justify='left' ) desc_label.pack(anchor='w') # 按钮组 button_frame = tk.Frame(self.main_frame, bg='white') button_frame.pack(pady=20) self.cloud_start_button = self.create_styled_button(button_frame, "🚀 启动加速", self.start_cloud_accel, "#27ae60", 15) self.cloud_start_button.pack(side='left', padx=10) self.cloud_stop_button = self.create_styled_button(button_frame, "⏹️ 停止加速", self.stop_cloud_accel, "#e74c3c", 15) self.cloud_stop_button.pack(side='left', padx=10) self.cloud_stop_button.config(state='disabled') # 状态显示 status_frame = tk.Frame(self.main_frame, bg='white') status_frame.pack(fill='x', pady=5) self.cloud_status_label = tk.Label( status_frame, text="📊 状态: 等待启动加速服务", font=("Microsoft YaHei", 10), fg="#7f8c8d", bg='white' ) self.cloud_status_label.pack() # 输出区域 output_card = tk.Frame(self.main_frame, bg='#f8f9fa', relief='raised', bd=1, padx=15, pady=15) output_card.pack(fill='both', expand=True, pady=(10, 0)) output_label = tk.Label(output_card, text="📋 加速服务日志:", font=("Microsoft YaHei", 10), bg='#f8f9fa') output_label.pack(anchor='w') self.cloud_output = scrolledtext.ScrolledText( output_card, height=12, width=80, font=("Consolas", 9), state='disabled', relief='solid', bd=1, bg='#ffffff' ) self.cloud_output.pack(fill='both', expand=True, pady=(5, 0)) # 初始信息 self.append_cloud_output("网盘加速服务就绪\n") self.append_cloud_output("点击「启动加速」开始服务\n") # 进程引用 self.cloud_process = None self.is_cloud_running = False self.nginx_process = None self.cloud_start_time = None # 二维码框架 qr_frame = tk.Frame(self.main_frame, bg='white') qr_frame.pack(pady=(0, 20)) # 二维码显示区域 qr_display_frame = tk.Frame(qr_frame, bg='white') qr_display_frame.pack(pady=10) # 生成并显示二维码 self.qr_label = tk.Label(qr_display_frame, bg='white') self.qr_label.pack() # 二维码标题 qr_title = tk.Label( qr_frame, text="📱 手机服务地址的扫描二维码", font=("Microsoft YaHei", 11, "bold"), fg="#2c3e50", bg='white' ) qr_title.pack() # 立即生成二维码 self.generate_qr_code(qr_url) # 恢复状态 if hasattr(self, 'cloud_output'): self.restore_page_state('cloud') def generate_qr_code(self, url): """生成二维码并显示""" try: # 创建二维码 qr = qrcode.QRCode( version=1, error_correction=qrcode.constants.ERROR_CORRECT_L, box_size=6, border=4, ) qr.add_data(url) qr.make(fit=True) # 生成二维码图像 qr_image = qr.make_image(fill_color="black", back_color="white") # 调整图像大小 qr_image = qr_image.resize((180, 180), Image.Resampling.LANCZOS) # 转换为PhotoImage photo = ImageTk.PhotoImage(qr_image) # 更新标签 self.qr_label.configure(image=photo) self.qr_label.image = photo # 保持引用 except Exception as e: self.append_cloud_output(f"[错误] 生成二维码失败: {e}\n") # 显示错误占位符 error_text = "❌ 二维码\n生成失败" self.qr_label.configure( text=error_text, font=("Microsoft YaHei", 8), fg="#e74c3c", bg="#f8f9fa", width=20, height=10, relief='solid', bd=1 ) # 修改所有页面切换方法,使用lambda包装 def setup_navigation(self): """设置页面导航""" # 在欢迎界面中 send_button = tk.Button(..., command=lambda: self.show_send_screen()) receive_button = tk.Button(..., command=lambda: self.show_receive_screen()) cloud_button = tk.Button(..., command=lambda: self.show_cloud_accel_screen()) # 在各个功能界面中 back_button = tk.Button(..., command=lambda: self.show_welcome_screen()) def create_kodcloud_ini(self): """创建kodcloud.ini配置文件""" # 获取当前时间,精确到秒 from datetime import datetime current_time = datetime.now().strftime("%Y%m%d_%H%M%S") ini_content = f"""[common] local_ip = 0.0.0.0 server_addr = 47.97.6.201 server_port = 7100 token = ENbOUMvXJGWuA623@@@ [kodcloud_{current_time}] type = xtcp role = visitor server_name = M0BUnkgzPxR0Ebdp9NQ4VUtm4EpTDvPR1 sk = aw1fohFnPcZEjRcRogENJzBbgPsNqjV31 bind_ip = 127.0.0.1 bind_port = 8089 """ try: with open('./kodcloud.ini', 'w', encoding='utf-8') as f: f.write(ini_content) self.append_cloud_output(f"✅ 配置文件 kodcloud.ini 创建成功\n") self.append_cloud_output(f"✅ 时间标识: {current_time}\n") return True except Exception as e: self.append_cloud_output(f"❌ 创建配置文件失败: {e}\n") return False def monitor_cloud_output(self): """监控网盘加速输出""" try: for line in iter(self.cloud_process.stdout.readline, ''): if not self.is_cloud_running: break if line: self.append_cloud_output(f"[加速服务] {line}") # 只有在服务正常运行时才记录时间(stop_cloud_accel中已经处理) if self.is_cloud_running: # 服务异常结束的情况 if self.cloud_start_time is not None: end_time = time.time() end_time_str = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(end_time)) duration = end_time - self.cloud_start_time duration_str = self.format_duration(duration) self.append_cloud_output(f"[时间] 加速服务异常停止: {end_time_str}\n") self.append_cloud_output(f"[统计] 运行时间: {duration_str}\n") self.append_cloud_output("❌ 加速服务异常终止\n") except Exception as e: self.append_cloud_output(f"[监控] 输出监控错误: {e}\n") finally: # 停止nginx服务(如果正在运行) self.stop_cloud_heartbeat() self.stop_nginx_service() def stop_nginx_service(self): """停止nginx服务""" if hasattr(self, 'nginx_process') and self.nginx_process and self.nginx_process.poll() is None: try: # 优雅停止nginx subprocess.run(['taskkill', '/f', '/im', 'nginx.exe'], text=True, encoding='gbk', timeout=5, capture_output=True, creationflags=subprocess.CREATE_NO_WINDOW) self.append_cloud_output("[Nginx] Web加速服务已停止\n") except: try: # 强制终止 self.nginx_process.terminate() self.nginx_process.wait(timeout=3) except: pass def start_cloud_accel(self): """启动网盘加速服务""" bport = 8089 # 检查端口是否被占用 occupying_pids = self.get_processes_using_port(bport) if occupying_pids: self.append_cloud_output(f"[警告] {bport}端口被占用请手动关闭以下进程或等待大约30s后重试启动加速服务:\n") for pid, process_name in occupying_pids: self.append_cloud_output(f"PID: {pid};进程: {process_name} \n") return try: # 重置开始时间 self.cloud_start_time = None # 创建配置文件 if not self.create_kodcloud_ini(): return # 记录开始时间 self.cloud_start_time = time.time() self.cloud_process = subprocess.Popen( ['.\\gc.exe', '-c', '.\\kodcloud.ini'], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, encoding='utf-8', errors='replace', bufsize=1, universal_newlines=True, creationflags=subprocess.CREATE_NO_WINDOW # 不创建窗口 ) self.page_states['cloud']['cloud_process'] = self.cloud_process # 启动nginx服务(如果存在nginx.exe) self.append_cloud_output("[Nginx] 启动Web加速服务...\n") self.nginx_process = subprocess.Popen( ['.\\ng\\nginx.exe', '-p', '.\\ng'], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, encoding='utf-8', errors='replace', bufsize=1, universal_newlines=True, creationflags=subprocess.CREATE_NO_WINDOW # 不创建窗口 ) # 等待服务启动 time.sleep(3) # 启动心跳 self.start_cloud_heartbeat() # 更新状态 self.is_cloud_running = True self.cloud_start_button.config(state='disabled') self.cloud_stop_button.config(state='normal') self.cloud_status_label.config(text="📊 状态: 加速服务运行中", fg="#27ae60") self.append_cloud_output("🚀 网盘加速服务启动成功!\n") # 获取本机IP并显示 local_ip = self.get_local_ip() self.append_cloud_output("📍 电脑访问地址: http://127.0.0.1:8089\n") self.append_cloud_output("📍 手机访问地址: http://{local_ip}:8088\n".format(local_ip=local_ip)) self.append_cloud_output("⏰ 服务运行中...\n") # 启动输出监控线程 output_thread = threading.Thread(target=self.monitor_cloud_output, daemon=True) output_thread.start() # 更新状态 self.page_states['cloud']['is_running'] = True except Exception as e: self.append_cloud_output(f"❌ 启动加速服务失败: {e}\n") def get_local_ip(self): """获取本机IP地址""" try: import socket # 创建一个UDP socket s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # 连接到一个远程地址(不会真正发送数据) s.connect(("8.8.8.8", 80)) ip_address = s.getsockname()[0] s.close() return ip_address except: return "无法获取IP地址" def stop_cloud_accel(self): """停止网盘加速服务""" self.is_cloud_running = False self.cloud_status_label.config(text="📊 状态: 正在停止服务", fg="#e74c3c") if self.cloud_process and self.cloud_process.poll() is None: try: self.cloud_process.terminate() self.append_cloud_output("⏹️ 正在停止加速服务...\n") except: pass # 停止nginx服务 self.stop_nginx_service() time.sleep(1) self.stop_cloud_heartbeat() time.sleep(1) # 记录结束时间(只有在成功启动过的情况下) if self.cloud_start_time is not None: try: end_time = time.time() end_time_str = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(end_time)) duration = end_time - self.cloud_start_time duration_str = self.format_duration(duration) self.append_cloud_output(f"[时间] 加速服务停止: {end_time_str}\n") self.append_cloud_output(f"[统计] 运行时间: {duration_str}\n") except Exception as e: self.append_cloud_output(f"[错误] 时间统计失败: {e}\n") self.cloud_start_button.config(state='normal') self.cloud_stop_button.config(state='disabled') self.cloud_status_label.config(text="📊 状态: 服务已停止", fg="#7f8c8d") self.append_cloud_output("✅ 加速服务已停止\n") # 更新状态 self.page_states['cloud']['is_running'] = False # 重置开始时间 self.cloud_start_time = None time.sleep(1) def append_cloud_output(self, message): """向网盘加速输出框添加消息""" self.cloud_output.config(state='normal') self.cloud_output.insert(tk.END, message) self.cloud_output.see(tk.END) self.cloud_output.config(state='disabled') # 更新状态存储 self.page_states['cloud']['output_content'] = self.cloud_output.get(1.0, tk.END) def create_styled_button(self, parent, text, command, bg_color, width=10): """创建样式统一的按钮""" return tk.Button( parent, text=text, command=command, font=("Microsoft YaHei", 10, "bold"), bg=bg_color, fg="white", width=width, relief='flat', cursor='hand2', padx=10, pady=5 ) def show_send_screen(self): """显示发送界面""" if self.current_page and self.current_page != 'send': # 检查当前页面是否有运行中的功能 if self.current_page == 'receive' and self.page_states['receive']['is_running']: self.show_confirm_dialog(self.stop_frpc_connection, self._show_send_screen) return elif self.current_page == 'cloud' and self.page_states['cloud']['is_running']: self.show_confirm_dialog(self.stop_cloud_accel, self._show_send_screen) return # 如果没有运行中的功能,直接显示发送界面 self._show_send_screen() def _show_send_screen(self): """显示发送界面""" if self.current_page: self.save_current_page_state() self.clear_frame() self.current_page = 'send' # 标题栏 title_frame = tk.Frame(self.main_frame, bg='white') title_frame.pack(fill='x', pady=(10, 20)) back_button = self.create_styled_button(title_frame, "← 返回", self.show_welcome_screen, "#95a5a6", 8) back_button.pack(side='left') title_label = tk.Label( title_frame, text="📤 文件发送", font=("Microsoft YaHei", 16, "bold"), fg="#2c3e50", bg='white' ) title_label.pack(side='left', padx=10) # 输入卡片 input_card = tk.Frame(self.main_frame, bg='#f8f9fa', relief='raised', bd=1, padx=20, pady=20) input_card.pack(fill='x', pady=(0, 15)) # 密钥输入 key_frame = tk.Frame(input_card, bg='#f8f9fa') key_frame.pack(fill='x', pady=5) tk.Label(key_frame, text="🔑 密钥:", font=("Microsoft YaHei", 10), bg='#f8f9fa').pack(side='left') self.send_key_entry = tk.Entry(key_frame, font=("Microsoft YaHei", 10), width=40, relief='solid', bd=1) self.send_key_entry.pack(side='left', padx=10) # 文件选择 file_frame = tk.Frame(input_card, bg='#f8f9fa') file_frame.pack(fill='x', pady=10) tk.Label(file_frame, text="📁 文件路径:", font=("Microsoft YaHei", 10), bg='#f8f9fa').pack(side='left') self.file_path_var = tk.StringVar() file_entry = tk.Entry(file_frame, textvariable=self.file_path_var, font=("Microsoft YaHei", 10), width=30, state='readonly', relief='solid', bd=1) file_entry.pack(side='left', padx=10) browse_button = self.create_styled_button(file_frame, "浏览文件", self.browse_file, "#8e44ad", 10) browse_button.pack(side='left') # 按钮组 button_frame = tk.Frame(self.main_frame, bg='white') button_frame.pack(pady=15) self.send_connect_button = self.create_styled_button(button_frame, "🔄 连接", self.start_send_connection, "#27ae60") self.send_connect_button.pack(side='left', padx=5) self.send_file_button = self.create_styled_button(button_frame, "🚀 发送文件", self.start_file_send, "#e74c3c") self.send_file_button.pack(side='left', padx=5) self.send_file_button.config(state='disabled') self.send_stop_button = self.create_styled_button(button_frame, "⏹️ 停止", self.stop_send_connection, "#95a5a6") self.send_stop_button.pack(side='left', padx=5) self.send_stop_button.config(state='disabled') # 状态显示 status_frame = tk.Frame(self.main_frame, bg='white') status_frame.pack(fill='x', pady=5) self.send_status_label = tk.Label( status_frame, text="📊 状态: 等待连接", font=("Microsoft YaHei", 10), fg="#7f8c8d", bg='white' ) self.send_status_label.pack() # 输出区域 output_card = tk.Frame(self.main_frame, bg='#f8f9fa', relief='raised', bd=1, padx=15, pady=15) output_card.pack(fill='both', expand=True, pady=(10, 0)) output_label = tk.Label(output_card, text="📋 发送日志:", font=("Microsoft YaHei", 10), bg='#f8f9fa') output_label.pack(anchor='w') self.send_output = scrolledtext.ScrolledText( output_card, height=12, width=80, font=("Consolas", 9), state='disabled', relief='solid', bd=1, bg='#ffffff' ) self.send_output.pack(fill='both', expand=True, pady=(5, 0)) # 初始信息 self.append_send_output("等待输入密钥并选择文件...\n") self.append_send_output("密钥格式应为: token|sk|psk|sern\n") # 进程引用 self.send_frpc_process = None self.send_p2p_process = None self.is_send_running = False # 初始化或恢复状态 if not hasattr(self, 'send_key_entry'): # 第一次创建界面 self.send_key_entry = tk.Entry(key_frame, font=("Microsoft YaHei", 10), width=40, relief='solid', bd=1) self.file_path_var = tk.StringVar() # ... 其他控件初始化 else: # 恢复状态 self.restore_page_state('send') def show_receive_screen(self): """显示接收界面""" if self.current_page and self.current_page != 'receive': # 检查当前页面是否有运行中的功能 if self.current_page == 'send' and self.page_states['send']['is_running']: self.show_confirm_dialog(self.stop_send_connection, self._show_receive_screen) return elif self.current_page == 'cloud' and self.page_states['cloud']['is_running']: self.show_confirm_dialog(self.stop_cloud_accel, self._show_receive_screen) return # 如果没有运行中的功能,直接显示接收界面 self._show_receive_screen() def _show_receive_screen(self): """显示接收界面""" if self.current_page: self.save_current_page_state() self.clear_frame() self.current_page = 'receive' # 标题栏 title_frame = tk.Frame(self.main_frame, bg='white') title_frame.pack(fill='x', pady=(10, 20)) back_button = self.create_styled_button(title_frame, "← 返回", self.show_welcome_screen, "#95a5a6", 8) back_button.pack(side='left') title_label = tk.Label( title_frame, text="📥 文件接收", font=("Microsoft YaHei", 16, "bold"), fg="#2c3e50", bg='white' ) title_label.pack(side='left', padx=10) # 输入卡片 input_card = tk.Frame(self.main_frame, bg='#f8f9fa', relief='raised', bd=1, padx=20, pady=20) input_card.pack(fill='x', pady=(0, 15)) # 密钥输入 key_frame = tk.Frame(input_card, bg='#f8f9fa') key_frame.pack(fill='x', pady=5) tk.Label(key_frame, text="🔑 密钥:", font=("Microsoft YaHei", 10), bg='#f8f9fa').pack(side='left') self.receive_key_entry = tk.Entry(key_frame, font=("Microsoft YaHei", 10), width=40, relief='solid', bd=1) self.receive_key_entry.pack(side='left', padx=10) # 保存路径选择 path_frame = tk.Frame(input_card, bg='#f8f9fa') path_frame.pack(fill='x', pady=10) tk.Label(path_frame, text="💾 保存路径:", font=("Microsoft YaHei", 10), bg='#f8f9fa').pack(side='left') self.receive_path_var = tk.StringVar() self.receive_path_var.set(".\\received_files") # 默认路径 path_entry = tk.Entry(path_frame, textvariable=self.receive_path_var, font=("Microsoft YaHei", 10), width=30, relief='solid', bd=1) path_entry.pack(side='left', padx=10) browse_path_button = self.create_styled_button(path_frame, "浏览文件夹", self.browse_receive_path, "#8e44ad", 12) browse_path_button.pack(side='left') # 按钮组 button_frame = tk.Frame(self.main_frame, bg='white') button_frame.pack(pady=15) self.connect_button = self.create_styled_button(button_frame, "🔄 连接", self.start_frpc_connection, "#27ae60") self.connect_button.pack(side='left', padx=5) self.stop_button = self.create_styled_button(button_frame, "⏹️ 停止", self.stop_frpc_connection, "#e74c3c") self.stop_button.pack(side='left', padx=5) self.stop_button.config(state='disabled') # 状态显示 status_frame = tk.Frame(self.main_frame, bg='white') status_frame.pack(fill='x', pady=5) self.status_label = tk.Label( status_frame, text="📊 状态: 等待连接", font=("Microsoft YaHei", 10), fg="#7f8c8d", bg='white' ) self.status_label.pack() # 输出区域 output_card = tk.Frame(self.main_frame, bg='#f8f9fa', relief='raised', bd=1, padx=15, pady=15) output_card.pack(fill='both', expand=True, pady=(10, 0)) output_label = tk.Label(output_card, text="📋 接收日志:", font=("Microsoft YaHei", 10), bg='#f8f9fa') output_label.pack(anchor='w') self.receive_output = scrolledtext.ScrolledText( output_card, height=12, width=80, font=("Consolas", 9), state='disabled', relief='solid', bd=1, bg='#ffffff' ) self.receive_output.pack(fill='both', expand=True, pady=(5, 0)) # 初始信息 self.append_output("等待输入密钥并连接...\n") self.append_output("密钥格式应为: token|sk|psk|sern\n") self.append_output(f"文件将保存到: {self.receive_path_var.get()}\n") # 进程引用 self.frpc_process = None self.p2p_process = None self.nginx_process = None self.is_running = False self.current_stage = "idle" # 初始化或恢复状态 if not hasattr(self, 'receive_key_entry'): # 第一次创建界面 self.receive_key_entry = tk.Entry(key_frame, font=("Microsoft YaHei", 10), width=40, relief='solid', bd=1) self.receive_path_var = tk.StringVar(value=".\\received_files") # ... 其他控件初始化 else: # 恢复状态 self.restore_page_state('receive') def browse_receive_path(self): """浏览选择接收文件保存路径""" folder_path = filedialog.askdirectory( title="选择文件保存目录", initialdir="." ) if folder_path: self.receive_path_var.set(folder_path) self.append_output(f"✅ 文件将保存到: {folder_path}\n") def browse_file(self): """浏览选择要发送的文件""" file_path = filedialog.askopenfilename( title="选择要发送的文件", filetypes=[ ("所有文件", "*.*"), ("文本文件", "*.txt"), ("图片文件", "*.jpg *.png *.gif"), ("视频文件", "*.mp4 *.avi *.mov"), ("文档文件", "*.pdf *.doc *.docx") ] ) if file_path: self.file_path_var.set(file_path) self.append_send_output(f"✅ 已选择文件: {file_path}\n") def create_send_ini(self, token, sk, sern, bport): """创建send.ini配置文件""" ini_content = f"""[common] local_ip = 0.0.0.0 server_addr = 47.97.6.201 server_port = 7100 token = {token} [xtcp_visitor] type = xtcp role = visitor server_name = {sern} sk = {sk} bind_ip = 127.0.0.1 bind_port = {bport} """ try: with open('./send.ini', 'w', encoding='utf-8') as f: f.write(ini_content) self.append_send_output(f"配置文件 send.ini 创建成功\n") return True except Exception as e: self.append_send_output(f"创建配置文件失败: {e}\n") return False def monitor_send_frpc_output(self): """监控发送NATCC输出""" try: for line in iter(self.send_frpc_process.stdout.readline, ''): if not self.is_send_running: break if line: self.append_send_output(f"[NATC] {line}") except: pass def monitor_send_p2p_output(self): """监控发送P2P输出""" try: for line in iter(self.send_p2p_process.stdout.readline, ''): if not self.is_send_running: break if line.strip(): self.append_send_output(f"[P2P] {line}") # 进程结束后检查退出码并记录结束时间 return_code = self.send_p2p_process.wait() end_time = time.time() end_time_str = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(end_time)) # 计算耗时 duration = end_time - self.send_start_time duration_str = self.format_duration(duration) if self.is_send_running: if return_code == 0: self.append_send_output(f"[时间] 文件发送完成: {end_time_str}\n") self.append_send_output(f"[统计] 总耗时: {duration_str}\n") self.append_send_output("✅ 文件发送成功完成!\n") else: self.append_send_output(f"[时间] 文件发送异常结束: {end_time_str}\n") self.append_send_output(f"[统计] 运行时间: {duration_str}\n") self.append_send_output(f"❌ 文件发送失败,退出码: {return_code}\n") self.send_file_button.config(state='normal') except Exception as e: self.append_send_output(f"[P2P] 输出监控错误: {e}\n") def execute_send_frpc(self): """执行发送NATCC命令""" try: self.update_send_status("正在启动NATC客户端...") self.send_frpc_process = subprocess.Popen( ['.\\gc.exe', '-c', '.\\send.ini'], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, encoding='utf-8', errors='replace', bufsize=1, universal_newlines=True, creationflags=subprocess.CREATE_NO_WINDOW # 不创建窗口 ) self.page_states['send']['p2p_process'] = self.send_p2p_process # 启动输出监控线程 output_thread = threading.Thread(target=self.monitor_send_frpc_output, daemon=True) output_thread.start() # 等待一段时间让NATCC建立连接 self.append_send_output("[NATC] 等待NATC连接建立...\n") time.sleep(3) # 检查NATCC进程是否还在运行 if self.send_frpc_process.poll() is not None: self.append_send_output("[NATC] NATC客户端意外退出\n") return # 如果还在运行,启用发送文件按钮 if self.is_send_running: self.append_send_output("[NATC] NATC连接已建立,可以发送文件\n") self.send_file_button.config(state='normal') self.update_send_status("就绪,可以发送文件") except Exception as e: self.append_send_output(f"[NATC] 执行错误: {e}\n") def execute_send_file(self): """执行文件发送""" try: self.update_send_status("正在发送文件...") file_path = self.file_path_var.get() if not file_path or not os.path.exists(file_path): self.append_send_output("[错误] 文件路径无效或文件不存在\n") return # 记录开始时间 self.send_start_time = time.time() start_time_str = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(self.send_start_time)) self.append_send_output(f"[时间] 文件发送开始: {start_time_str}\n") startupinfo = subprocess.STARTUPINFO() startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW startupinfo.wShowWindow = 0 file_path = self.file_path_var.get() file_size = os.path.getsize(file_path) file_name = os.path.basename(file_path) self.append_send_output(f"[文件] 名称: {file_name}\n") self.append_send_output(f"[文件] 大小: {self.format_file_size(file_size)}\n") self.send_p2p_process = subprocess.Popen( [ '.\\python.exe', '.\\p2pfile.py', '--mode', 'send', '--path', file_path, '--psk', self.send_psk, '--port', self.bport ], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, encoding='gbk', errors='replace', bufsize=1, universal_newlines=True, creationflags=subprocess.CREATE_NO_WINDOW # 不创建窗口 ) self.append_send_output(f"[P2P] 开始发送文件: {os.path.basename(file_path)}\n") # 启动输出监控线程 output_thread = threading.Thread(target=self.monitor_send_p2p_output, daemon=True) output_thread.start() except Exception as e: self.append_send_output(f"[P2P] 执行错误: {e}\n") def start_send_connection(self): """开始发送连接""" key = self.send_key_entry.get().strip() if not key: messagebox.showwarning("输入错误", "请输入密钥") return # 解析密钥 if '|' not in key: messagebox.showwarning("格式错误", "密钥格式应为: token|sk|psk|sern") return parts = key.split('|') if len(parts) < 4: messagebox.showwarning("格式错误", "密钥格式应为: token|sk|psk|sern") return token = parts[0].strip() sk = parts[1].strip() psk = parts[2].strip() sern = parts[3].strip() bport = parts[4].strip() if len(parts) > 4 else "7001" self.send_token = token self.send_sk = sk self.send_psk = psk self.send_sern = sern self.bport = bport # 检查7001端口是否被占用 occupying_pids = self.get_processes_using_port(bport) if occupying_pids: self.append_send_output(f"[警告] {bport}端口被以下进程占用:\n") for pid, process_name in occupying_pids: self.append_send_output(f" - PID: {pid}, 进程: {process_name}\n") self.append_send_output("[提示] 请手动关闭这些进程或更改端口配置后再试\n") return # 创建配置文件 if not self.create_send_ini(token, sk, sern, bport): return # 清空输出框 self.send_output.config(state='normal') self.send_output.delete(1.0, tk.END) self.send_output.config(state='disabled') self.append_send_output(f"开始连接流程...\n") self.append_send_output(f"Token: {token}\n") self.append_send_output(f"SK: {sk}\n") self.append_send_output(f"PSK: {psk}\n") self.append_send_output(f"SERN: {sern}\n") self.append_send_output(f"BPORT: {bport}\n") self.append_send_output("-" * 50 + "\n") # 禁用连接按钮,启用停止按钮 self.send_connect_button.config(state='disabled') self.send_stop_button.config(state='normal') # 设置运行标志 self.is_send_running = True self.update_send_status("启动中...") # 在新线程中执行NATCC self.send_process_thread = threading.Thread(target=self.execute_send_frpc, daemon=True) self.send_process_thread.start() # 更新状态 self.page_states['send']['is_running'] = True self.page_states['send']['key_entry_text'] = key self.page_states['send']['file_path'] = self.file_path_var.get() def get_processes_using_port(self, port): """获取占用指定端口的进程信息""" try: if os.name == 'nt': # Windows系统 return self._get_processes_windows(port) else: # Linux/Mac系统 self.append_send_output(f"linux系统") return [] except Exception as e: self.append_send_output(f"[错误] 获取端口信息时出错: {e}\n") return [] def _get_processes_windows(self, port): """Windows系统下获取占用端口的进程信息""" processes = [] try: # 方法1: 使用netstat命令 result = subprocess.run( ['netstat', '-ano', '-p', 'tcp'], capture_output=True, text=True, encoding='gbk', creationflags=subprocess.CREATE_NO_WINDOW, # 不创建窗口 timeout=10 ) lines = result.stdout.split('\n') for line in lines: if f':{port}' in line and 'LISTENING' in line: parts = line.split() if len(parts) >= 5: pid = parts[-1].strip() process_name = self._get_process_name_windows(pid) processes.append((pid, process_name)) # 方法2: 使用PowerShell备用 if not processes: processes = self._get_processes_windows_ps(port) except Exception as e: self.append_send_output(f"[错误] Windows端口检测失败: {e}\n") return processes def _get_processes_windows_ps(self, port): """Windows使用PowerShell获取进程信息""" processes = [] try: ps_command = f"Get-NetTCPConnection -LocalPort {port} | Select-Object OwningProcess, @{{Name='ProcessName'; Expression={{ (Get-Process -Id $_.OwningProcess).Name }} }}" result = subprocess.run( ['powershell', '-Command', ps_command], capture_output=True, text=True, timeout=10,creationflags=subprocess.CREATE_NO_WINDOW # 不创建窗口 ) if result.returncode == 0: lines = result.stdout.strip().split('\n') for line in lines: if 'OwningProcess' in line and 'ProcessName' in line: continue # 跳过标题行 parts = line.split() if len(parts) >= 2: pid = parts[0].strip() process_name = parts[1].strip() if len(parts) > 1 else "未知进程" processes.append((pid, process_name)) except Exception as e: self.append_send_output(f"[错误] PowerShell检测失败: {e}\n") return processes def _get_process_name_windows(self, pid): """Windows下根据PID获取进程名称""" try: result = subprocess.run( ['tasklist', '/fi', f'pid eq {pid}', '/fo', 'csv', '/nh'], capture_output=True, text=True, encoding='gbk',creationflags=subprocess.CREATE_NO_WINDOW # 不创建窗口 ) if result.returncode == 0 and result.stdout.strip(): # CSV格式: "进程名","PID","会话名","会话#","内存使用" parts = result.stdout.strip().split('","') if len(parts) >= 1: process_name = parts[0].replace('"', '') return process_name except: pass return "未知进程" def start_file_send(self): """开始发送文件""" if not self.file_path_var.get(): messagebox.showwarning("选择错误", "请先选择要发送的文件") return # 禁用发送按钮,避免重复点击 self.send_file_button.config(state='disabled') # 在新线程中执行文件发送 send_thread = threading.Thread(target=self.execute_send_file, daemon=True) send_thread.start() def stop_send_connection(self): """停止发送连接""" self.is_send_running = False self.update_send_status("正在停止...") # 停止NATCC进程 if self.send_frpc_process and self.send_frpc_process.poll() is None: try: self.send_frpc_process.terminate() self.append_send_output("[NATC] 正在停止NATC客户端...\n") except: pass # 停止P2P进程 if self.send_p2p_process and self.send_p2p_process.poll() is None: try: self.send_p2p_process.terminate() self.append_send_output("[P2P] 正在停止文件发送...\n") except: pass time.sleep(1) self.send_connect_button.config(state='normal') self.send_file_button.config(state='disabled') self.send_stop_button.config(state='disabled') self.update_send_status("已停止") self.append_send_output("所有进程已停止\n") # 更新状态 self.page_states['send']['is_running'] = False def update_send_status(self, status): """更新发送状态标签""" self.send_status_label.config(text=f"状态: {status}") def append_send_output(self, message): """向发送输出框添加消息""" self.send_output.config(state='normal') self.send_output.insert(tk.END, message) self.send_output.see(tk.END) self.send_output.config(state='disabled') # 更新状态存储 self.page_states['send']['output_content'] = self.send_output.get(1.0, tk.END) def create_service_ini(self, token, sk, sern, bport): """创建service.ini配置文件""" ini_content = f"""[common] server_addr = 47.97.6.201 server_port = 7100 token = {token} [{sern}] type = xtcp sk = {sk} local_ip = 127.0.0.1 local_port = {bport} """ try: with open('./service.ini', 'w', encoding='utf-8') as f: f.write(ini_content) self.append_output(f"配置文件 service.ini 创建成功\n") return True except Exception as e: self.append_output(f"创建配置文件失败: {e}\n") return False # 在所有的 subprocess.Popen 调用中添加 CREATE_NO_WINDOW 标志 def execute_frpc(self): """执行frpc命令""" try: self.current_stage = "frpc" self.update_status("正在启动NATC客户端...") self.frpc_process = subprocess.Popen( ['.\\gc.exe', '-c', '.\\service.ini'], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, encoding='utf-8', errors='replace', bufsize=1, universal_newlines=True, creationflags=subprocess.CREATE_NO_WINDOW # 不创建窗口 ) self.page_states['receive']['frpc_process'] = self.frpc_process # 启动输出监控线程 output_thread = threading.Thread(target=self.monitor_frpc_output, daemon=True) output_thread.start() # 等待一段时间让NATCC建立连接 self.append_output("[NATC] 等待NATC连接建立...\n") time.sleep(3) # 等待3秒让NATCC建立连接 # 检查NATCC进程是否还在运行 if self.frpc_process.poll() is not None: self.append_output("[NATC] NATC客户端意外退出\n") return # 如果还在运行,启动P2P if self.is_running: self.append_output("[NATC] NATC连接已建立,启动P2P接收器...\n") self.execute_p2p_receiver() except Exception as e: self.append_output(f"[NATC] 执行错误: {e}\n") def monitor_frpc_output(self): """监控NATCC输出""" try: for line in iter(self.frpc_process.stdout.readline, ''): if not self.is_running: break if line: self.append_output(f"[NATC] {line}") except: pass def monitor_p2p_output(self): """监控P2P输出并检测进程结束""" try: for line in iter(self.p2p_process.stdout.readline, ''): if not self.is_running: break if line.strip(): self.append_output(f"[P2P] {line}") # 进程结束后检查退出码并记录结束时间 return_code = self.p2p_process.wait() end_time = time.time() end_time_str = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(end_time)) # 计算耗时 duration = end_time - self.receive_start_time duration_str = self.format_duration(duration) if self.is_running: if return_code == 0: self.append_output(f"[时间] 文件接收完成: {end_time_str}\n") self.append_output(f"[统计] 总耗时: {duration_str}\n") self.append_output("✅ 文件接收成功完成!\n") else: self.append_output(f"[时间] 文件接收异常结束: {end_time_str}\n") self.append_output(f"[统计] 运行时间: {duration_str}\n") self.append_output(f"❌ 文件接收失败,退出码: {return_code}\n") except Exception as e: self.append_output(f"[P2P] 输出监控错误: {e}\n") def format_file_size(self, size_bytes): """格式化文件大小""" if size_bytes == 0: return "0B" size_names = ["B", "KB", "MB", "GB", "TB"] i = 0 size = size_bytes while size >= 1024 and i < len(size_names) - 1: size /= 1024 i += 1 return f"{size:.2f} {size_names[i]}" def format_duration(self, seconds): """格式化时间间隔""" if seconds < 60: return f"{seconds:.1f}秒" elif seconds < 3600: minutes = seconds // 60 seconds = seconds % 60 return f"{int(minutes)}分{int(seconds)}秒" else: hours = seconds // 3600 minutes = (seconds % 3600) // 60 seconds = seconds % 60 return f"{int(hours)}时{int(minutes)}分{int(seconds)}秒" def execute_p2p_receiver(self): """执行P2P文件接收器""" try: self.current_stage = "p2p" self.update_status("正在启动P2P文件接收...") # 记录开始时间 self.receive_start_time = time.time() start_time_str = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(self.receive_start_time)) self.append_output(f"[时间] 文件接收开始: {start_time_str}\n") # 获取用户选择的保存路径 save_path = self.receive_path_var.get().strip() if not save_path: save_path = ".\\received_files" # 创建目录(如果不存在) os.makedirs(save_path, exist_ok=True) self.p2p_process = subprocess.Popen( [ '.\\python.exe', '.\\p2pfile.py', '--mode', 'recv', '--psk', self.current_psk, '--outdir', save_path, '--port', self.bport ], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, encoding='gbk', errors='replace', bufsize=1, universal_newlines=True, creationflags=subprocess.CREATE_NO_WINDOW # 不创建窗口 ) self.page_states['receive']['p2p_process'] = self.p2p_process self.append_output("[P2P] 启动P2P文件接收器...\n") self.append_output(f"[P2P] 使用PSK: {self.current_psk}\n") self.append_output(f"[P2P] 文件保存路径: {save_path}\n") # 启动输出监控线程 output_thread = threading.Thread(target=self.monitor_p2p_output, daemon=True) output_thread.start() # 检查进程是否立即失败 time.sleep(1) if self.p2p_process.poll() is not None: self.append_output("[P2P] P2P接收器启动失败\n") except Exception as e: self.append_output(f"[P2P] 执行错误: {e}\n") def start_frpc_connection(self): """开始NATC连接和P2P接收""" key = self.receive_key_entry.get().strip() if not key: messagebox.showwarning("输入错误", "请输入密钥") return # 解析密钥 if '|' not in key: messagebox.showwarning("格式错误", "密钥格式应为: token|sk|psk|sern") return parts = key.split('|') if len(parts) < 4: messagebox.showwarning("格式错误", "密钥格式应为: token|sk|psk|sern") return token = parts[0].strip() sk = parts[1].strip() psk = parts[2].strip() sern = parts[3].strip() # 修改: 当缺少bport时,默认使用7001 bport = parts[4].strip() if len(parts) > 4 else "6000" self.current_token = token self.current_sk = sk self.current_psk = psk self.current_sern = sern self.bport = bport # 检查6000端口是否被占用 occupying_pids = self.get_processes_using_port(bport) if occupying_pids: self.append_send_output(f"[警告] {bport}端口被以下进程占用:\n") for pid, process_name in occupying_pids: self.append_send_output(f" - PID: {pid}, 进程: {process_name}\n") self.append_send_output("[提示] 请手动关闭这些进程或更改端口配置后再试\n") return # 获取保存路径并显示 save_path = self.receive_path_var.get() self.append_output(f"📁 保存路径: {save_path}\n") # 创建配置文件 if not self.create_service_ini(token, sk, sern, bport): return # 清空输出框 self.receive_output.config(state='normal') self.receive_output.delete(1.0, tk.END) self.receive_output.config(state='disabled') self.append_output(f"开始连接流程...\n") self.append_output(f"Token: {token}\n") self.append_output(f"SK: {sk}\n") self.append_output(f"PSK: {psk}\n") self.append_output(f"SERN: {sern}\n") self.append_output(f"保存路径: {save_path}\n") self.append_output(f"port: {bport}\n") self.append_output("-" * 50 + "\n") # 禁用连接按钮,启用停止按钮 self.connect_button.config(state='disabled') self.stop_button.config(state='normal') # 设置运行标志 self.is_running = True self.update_status("启动中...") # 在新线程中执行 self.process_thread = threading.Thread(target=self.execute_frpc, daemon=True) self.process_thread.start() # 更新状态 self.page_states['receive']['is_running'] = True self.page_states['receive']['key_entry_text'] = key self.page_states['receive']['save_path'] = self.receive_path_var.get() def stop_frpc_connection(self): """停止所有进程""" self.is_running = False self.update_status("正在停止...") # 停止NATCC进程 if self.frpc_process and self.frpc_process.poll() is None: try: self.frpc_process.terminate() self.append_output("[NATC] 正在停止NATC客户端...\n") except: pass # 停止P2P进程 if self.p2p_process and self.p2p_process.poll() is None: try: self.p2p_process.terminate() self.append_output("[P2P] 正在停止P2P接收器...\n") except: pass time.sleep(1) # 给进程一些时间结束 self.connect_button.config(state='normal') self.stop_button.config(state='disabled') self.update_status("已停止") self.append_output("所有进程已停止\n") self.page_states['receive']['is_running'] = False def update_status(self, status): """更新状态标签""" self.status_label.config(text=f"状态: {status}") def append_output(self, message): """向输出框添加消息""" self.receive_output.config(state='normal') self.receive_output.insert(tk.END, message) self.receive_output.see(tk.END) # 自动滚动到底部 self.receive_output.config(state='disabled') # 更新状态存储 self.page_states['receive']['output_content'] = self.receive_output.get(1.0, tk.END) def start_cloud_heartbeat(self): """启动网盘加速心跳""" try: self.cloud_heartbeat_manager = CloudHeartbeatManager(self.append_cloud_output, interval=30) self.cloud_heartbeat_manager.start() self.append_cloud_output("[心跳] 🎯 服务心跳已启动,间隔30秒\n") except Exception as e: self.append_cloud_output(f"[心跳] 启动失败: {e}\n") def stop_cloud_heartbeat(self): """停止网盘加速心跳""" try: if self.cloud_heartbeat_manager: self.cloud_heartbeat_manager.stop() self.cloud_heartbeat_manager = None except Exception as e: self.append_cloud_output(f"[心跳] 停止失败: {e}\n") # 创建主窗口并运行程序 if __name__ == "__main__": root = tk.Tk() app = FileTransferApp(root) root.mainloop()