You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1615 lines
61 KiB
1615 lines
61 KiB
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('<Return>', lambda e: on_confirm())
|
|
confirm_dialog.bind('<Escape>', 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()
|