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 from subprocess import CREATE_NO_WINDOW, STARTUPINFO, STARTF_USESHOWWINDOW class FileTransferApp: def __init__(self, root): self.root = root self.root.title("蜗牛创造的高速文件传输") self.root.geometry("900x700") 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.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': 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': 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': 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': 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']) 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': 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']) 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': 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)) desc_text = """功能说明: • 为KodCloud等网盘提供加速服务 • 使用XTCP协议建立高速通道 • 本地端口: 8089 • 服务器: 127.0.0.1""" 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 # 恢复状态 if hasattr(self, 'cloud_output'): self.restore_page_state('cloud') # 修改所有页面切换方法,使用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}") except: pass def start_cloud_accel(self): """启动网盘加速服务""" bport = 8089 # 检查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 try: # 创建配置文件 if not self.create_kodcloud_ini(): return 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 # 更新状态 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") self.append_cloud_output("📍 本地访问地址: http://127.0.0.1:8089\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 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 time.sleep(1) 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 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.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 # 不创建窗口 ) 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) # 创建主窗口并运行程序 if __name__ == "__main__": root = tk.Tk() app = FileTransferApp(root) root.mainloop()