最近重新部署了frp,使用的是最新的0.67.0版本,之前有写过一篇frp部署的文章但是太老了,有需要中文frp介绍的可以去这个地址看下:https://gofrp.org/zh-cn/docs/overview/ 我的程序是在github上的地址下载的 https://github.com/fatedier/frp/tags(注意服务端、客户端版本用同一个版本)
本次借助Qoder直接在windows下写了个frpc的可视化配置客户端,直接看成品吧:

先附上一些所需文件/代码
frpc_manager_gui.py
import tkinter as tkfrom tkinter import ttk, scrolledtext, messageboximport subprocessimport threadingimport osimport sysimport configparserclass FRPCClientGUI:def __init__(self, root):self.root = rootself.root.title("FRP 客户端管理器")self.root.geometry("700x600")# 进程对象self.process = Noneself.output_thread = Noneself.is_running = Falseself.startup_status = "starting" # starting, success, failed# 配置文件路径self.config_file = os.path.join(os.path.dirname(os.path.abspath(__file__)), "frpc_manager.ini")self.success_config_file = os.path.join(os.path.dirname(os.path.abspath(__file__)), "frpc_last_success.ini")# 创建主框架self.create_widgets()# 加载配置self.load_config()# 程序启动后自动触发启动self.root.after(500, self.auto_start_frpc)def create_widgets(self):"""创建界面组件"""# 标题title_label = ttk.Label(self.root, text="FRP 客户端配置", font=("Arial", 16, "bold"))title_label.pack(pady=5)# 配置框架(默认先创建,后面根据配置决定是否隐藏)self.config_frame = ttk.LabelFrame(self.root, text="配置参数", padding=10)self.config_frame.pack(fill="x", padx=20, pady=5)# 第一行:FRP_SERVER_ADDR 和 FRP_SERVER_PORTttk.Label(self.config_frame, text="FRP 服务器地址:").grid(row=0, column=0, sticky="w", pady=5, padx=5)self.server_addr_entry = ttk.Entry(self.config_frame, width=20)self.server_addr_entry.grid(row=0, column=1, padx=5, pady=5)self.server_addr_entry.bind("<KeyRelease>", lambda e: self.update_config_description())ttk.Label(self.config_frame, text="FRP 服务器端口:").grid(row=0, column=2, sticky="w", pady=5, padx=5)self.server_port_entry = ttk.Entry(self.config_frame, width=20)self.server_port_entry.grid(row=0, column=3, padx=5, pady=5)# 第二行:FRP_TOKEN 和 FRP_NAMEttk.Label(self.config_frame, text="FRP Token:").grid(row=1, column=0, sticky="w", pady=5, padx=5)self.token_entry = ttk.Entry(self.config_frame, width=20, show="*")self.token_entry.grid(row=1, column=1, padx=5, pady=5)ttk.Label(self.config_frame, text="FRP 代理名称:").grid(row=1, column=2, sticky="w", pady=5, padx=5)self.name_entry = ttk.Entry(self.config_frame, width=20)self.name_entry.grid(row=1, column=3, padx=5, pady=5)# 第三行:LOCAL_IP 和 LOCAL_PORTttk.Label(self.config_frame, text="局域网主机 IP:").grid(row=2, column=0, sticky="w", pady=5, padx=5)self.local_ip_entry = ttk.Entry(self.config_frame, width=20)self.local_ip_entry.grid(row=2, column=1, padx=5, pady=5)self.local_ip_entry.bind("<KeyRelease>", lambda e: self.update_config_description())ttk.Label(self.config_frame, text="局域网主机端口:").grid(row=2, column=2, sticky="w", pady=5, padx=5)self.local_port_entry = ttk.Entry(self.config_frame, width=20)self.local_port_entry.grid(row=2, column=3, padx=5, pady=5)self.local_port_entry.bind("<KeyRelease>", lambda e: self.update_config_description())# 第四行:FRP_REMOTE_PORT 和 FRP_USER (可选)ttk.Label(self.config_frame, text="FRP 远程端口:").grid(row=3, column=0, sticky="w", pady=5, padx=5)self.remote_port_entry = ttk.Entry(self.config_frame, width=20)self.remote_port_entry.grid(row=3, column=1, padx=5, pady=5)self.remote_port_entry.bind("<KeyRelease>", lambda e: self.update_config_description())ttk.Label(self.config_frame, text="FRP 用户 (可选):").grid(row=3, column=2, sticky="w", pady=5, padx=5)self.user_entry = ttk.Entry(self.config_frame, width=20)self.user_entry.grid(row=3, column=3, padx=5, pady=5)# 第五行:配置说明标签self.desc_label = ttk.Label(self.config_frame, text="", foreground="gray")self.desc_label.grid(row=4, column=0, columnspan=4, sticky="w", padx=10, pady=5)# 按钮框架button_frame = ttk.Frame(self.root)button_frame.pack(fill="x", padx=20, pady=10)# 启动按钮self.start_button = ttk.Button(button_frame, text="▶ 启动 FRPC", command=self.start_frpc)self.start_button.pack(side="left", padx=5)# 停止按钮self.stop_button = ttk.Button(button_frame, text="◼ 停止 FRPC", command=self.stop_frpc, state="disabled")self.stop_button.pack(side="left", padx=5)# 状态标签self.status_label = ttk.Label(button_frame, text="状态:已停止", foreground="red")self.status_label.pack(side="left", padx=20)# 日志框架log_frame = ttk.LabelFrame(self.root, text="FRPC 控制台输出", padding=10)log_frame.pack(fill="both", expand=True, padx=20, pady=5)# 日志文本区域self.log_text = scrolledtext.ScrolledText(log_frame, wrap=tk.WORD, height=20, bg="black", fg="green", font=("Consolas", 9))self.log_text.pack(fill="both", expand=True)# 清空日志按钮clear_button = ttk.Button(self.root, text="清空日志", command=self.clear_log)clear_button.pack(pady=5)def start_frpc(self):"""启动 FRPC 客户端"""if self.is_running:messagebox.showwarning("警告", "FRPC 已经在运行中!")return# 先保存配置self.save_config()# 获取配置值server_addr = self.server_addr_entry.get().strip()server_port = self.server_port_entry.get().strip()token = self.token_entry.get().strip()remote_port = self.remote_port_entry.get().strip()name = self.name_entry.get().strip()local_ip = self.local_ip_entry.get().strip()local_port = self.local_port_entry.get().strip()user = self.user_entry.get().strip()# 验证必填字段if not server_addr or not server_port or not token or not remote_port or not name or not local_ip or not local_port:messagebox.showerror("错误", "请填写所有必填字段!")return# 设置环境变量env = os.environ.copy()env["FRP_SERVER_ADDR"] = server_addrenv["FRP_SERVER_PORT"] = server_portenv["FRP_TOKEN"] = tokenenv["FRP_REMOTE_PORT"] = remote_portenv["FRP_NAME"] = nameenv["LOCAL_IP"] = local_ipenv["LOCAL_PORT"] = local_portif user:env["FRP_USER"] = user# 获取 frpc.exe 路径frpc_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "frpc", "frpc.exe")if not os.path.exists(frpc_path):messagebox.showerror("错误", f"未找到 frpc.exe 文件:n{frpc_path}")return# 配置文件路径config_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "frpc", "frpc.env.toml")try:# 启动进程self.process = subprocess.Popen([frpc_path, "-c", config_path],stdout=subprocess.PIPE,stderr=subprocess.STDOUT,stdin=subprocess.PIPE,env=env,cwd=os.path.dirname(frpc_path),creationflags=subprocess.CREATE_NO_WINDOW if sys.platform == "win32" else 0)self.is_running = Trueself.start_button.config(state="disabled")self.stop_button.config(state="normal")self.status_label.config(text="状态:运行中", foreground="green")self.append_log(f"=== FRPC 已启动 ===")self.append_log(f"服务器地址:{server_addr}:{server_port}")self.append_log(f"远程端口:{remote_port}")self.append_log(f"代理名称:{name}")self.append_log(f"本地地址:{local_ip}:{local_port}")self.append_log(f"进程 ID: {self.process.pid}")self.append_log("=" * 50)# 重置启动状态self.startup_status = "starting"# 启动读取线程self.output_thread = threading.Thread(target=self.read_output, daemon=True)self.output_thread.start()except Exception as e:messagebox.showerror("错误", f"启动 FRPC 失败:n{str(e)}")self.is_running = Falseself.start_button.config(state="normal")self.stop_button.config(state="disabled")def stop_frpc(self):"""停止 FRPC 客户端"""if not self.is_running:messagebox.showwarning("警告", "FRPC 未运行!")returnconfirm = messagebox.askyesno("确认", "确定要停止 FRPC 吗?")if not confirm:returntry:if self.process:self.process.terminate()try:self.process.wait(timeout=5)except subprocess.TimeoutExpired:self.process.kill()self.append_log("n=== FRPC 已停止 ===")self.is_running = Falseself.process = Noneexcept Exception as e:self.append_log(f"停止时出错:{str(e)}")finally:self.is_running = Falseself.start_button.config(state="normal")self.stop_button.config(state="disabled")self.status_label.config(text="状态:已停止", foreground="red")def read_output(self):"""读取 FRPC 输出并判断启动状态"""if self.process and self.process.stdout:for line in iter(self.process.stdout.readline, b''):try:decoded_line = line.decode('utf-8', errors='ignore').strip()if decoded_line:# 使用 after 方法在主线程中更新 UIself.root.after(0, lambda l=decoded_line: self.append_log(l))# 判断启动状态self.root.after(0, lambda l=decoded_line: self.check_startup_status(l))except Exception as e:self.root.after(0, lambda: self.append_log(f"读取输出时出错:{str(e)}"))def append_log(self, message):"""添加日志消息"""self.log_text.insert(tk.END, message + "n")self.log_text.see(tk.END) # 自动滚动到底部def check_startup_status(self, line):"""检查启动状态"""if self.startup_status == "starting":# 检测是否启动成功if "start proxy success" in line.lower():self.startup_status = "success"self.append_log("n✓ FRPC 启动成功!")self.status_label.config(text="状态:已启动", foreground="#00aa00")# 保存成功的配置self.save_success_config()# 检测是否启动失败(代理已存在)elif "already exists" in line.lower() or "error" in line.lower():self.startup_status = "failed"self.append_log(f"n✗ FRPC 启动失败:代理可能已存在/网络异常/鉴权失败")self.status_label.config(text="状态:启动失败", foreground="red")# 延迟后自动停止进程self.root.after(1000, self.auto_stop_on_failure)def auto_stop_on_failure(self):"""启动失败后自动停止进程"""if self.startup_status == "failed" and self.is_running:self.append_log("正在停止 FRPC 进程...")try:if self.process:self.process.terminate()try:self.process.wait(timeout=5)except subprocess.TimeoutExpired:self.process.kill()self.process = Noneexcept Exception as e:self.append_log(f"停止进程时出错:{str(e)}")finally:self.is_running = Falseself.start_button.config(state="normal")self.stop_button.config(state="disabled")def clear_log(self):"""清空日志"""self.log_text.delete(1.0, tk.END)def update_config_description(self):"""更新配置说明文字"""server_addr = self.server_addr_entry.get().strip() or "{FRP 服务器地址}"remote_port = self.remote_port_entry.get().strip() or "{FRP 远程端口}"local_ip = self.local_ip_entry.get().strip() or "{局域网主机 IP}"local_port = self.local_port_entry.get().strip() or "{局域网主机端口}"desc_text = f"访问【{server_addr}:{remote_port}】> 将会转发到本机可访问的地址 > 【{local_ip}:{local_port}】"self.desc_label.config(text=desc_text)def auto_start_frpc(self):"""程序启动时自动启动 FRPC"""# 检查配置文件是否存在且有值if not os.path.exists(self.config_file):returnconfig = configparser.ConfigParser()try:config.read(self.config_file, encoding='utf-8')# 检查是否有必填字段if 'FRP' in config:frp = config['FRP']has_required = (frp.get('server_addr', '').strip() andfrp.get('server_port', '').strip() andfrp.get('token', '').strip() andfrp.get('remote_port', '').strip() andfrp.get('name', '').strip() andfrp.get('local_ip', '').strip() andfrp.get('local_port', '').strip())if has_required:self.append_log("=== 自动启动 FRPC ===")self.start_frpc()except Exception as e:self.append_log(f"自动启动检查失败:{str(e)}")def load_config(self):"""从 INI 配置文件加载配置"""if not os.path.exists(self.config_file):# 如果配置文件不存在,创建空配置文件self.create_default_config()returnconfig = configparser.ConfigParser()try:config.read(self.config_file, encoding='utf-8')# 读取 UI 显示配置(默认不显示)show_config = False # 默认不显示配置区域if 'UI' in config:ui = config['UI']show_config_str = ui.get('show_config', '0').strip()# 只有明确设置为 1 才显示show_config = (show_config_str == '1')# 根据配置显示/隐藏配置区域if show_config:self.config_frame.pack(fill="x", padx=20, pady=5)else:self.config_frame.pack_forget()# 读取 FRP 配置if 'FRP' in config:frp = config['FRP']self.server_addr_entry.insert(0, frp.get('server_addr', ''))self.server_port_entry.insert(0, frp.get('server_port', ''))self.token_entry.insert(0, frp.get('token', ''))self.remote_port_entry.insert(0, frp.get('remote_port', ''))self.name_entry.insert(0, frp.get('name', ''))self.local_ip_entry.insert(0, frp.get('local_ip', ''))self.local_port_entry.insert(0, frp.get('local_port', ''))self.user_entry.insert(0, frp.get('user', ''))# 更新配置说明self.update_config_description()except Exception as e:messagebox.showerror("错误", f"读取配置文件失败:n{str(e)}")def create_default_config(self):"""创建默认配置文件"""config = configparser.ConfigParser()config['FRP'] = {'server_addr': '','server_port': '','token': '','remote_port': '','name': '','local_ip': '','local_port': '','user': ''}config['UI'] = {'show_config': '0' # 默认不显示配置区域}try:with open(self.config_file, 'w', encoding='utf-8') as f:config.write(f)messagebox.showinfo("提示", "已创建配置文件:n" + self.config_file + "nn请在配置文件中填写参数后重新启动程序。")except Exception as e:messagebox.showerror("错误", f"创建配置文件失败:n{str(e)}")def save_config(self):"""保存配置到 INI 文件"""config = configparser.ConfigParser()config['FRP'] = {'server_addr': self.server_addr_entry.get().strip(),'server_port': self.server_port_entry.get().strip(),'token': self.token_entry.get().strip(),'remote_port': self.remote_port_entry.get().strip(),'name': self.name_entry.get().strip(),'local_ip': self.local_ip_entry.get().strip(),'local_port': self.local_port_entry.get().strip(),'user': self.user_entry.get().strip()}# 保留原有的 UI 配置,不修改用户的设置if os.path.exists(self.config_file):old_config = configparser.ConfigParser()try:old_config.read(self.config_file, encoding='utf-8')# 只复制 UI 节,不做任何修改if 'UI' in old_config:config['UI'] = {}for key, value in old_config['UI'].items():config['UI'][key] = valueexcept:passtry:with open(self.config_file, 'w', encoding='utf-8') as f:config.write(f)except Exception as e:self.append_log(f"保存配置文件失败:{str(e)}")def save_success_config(self):"""保存最后一次成功启动的配置"""config = configparser.ConfigParser()config['FRP'] = {'server_addr': self.server_addr_entry.get().strip(),'server_port': self.server_port_entry.get().strip(),'token': self.token_entry.get().strip(),'remote_port': self.remote_port_entry.get().strip(),'name': self.name_entry.get().strip(),'local_ip': self.local_ip_entry.get().strip(),'local_port': self.local_port_entry.get().strip(),'user': self.user_entry.get().strip()}try:with open(self.success_config_file, 'w', encoding='utf-8') as f:config.write(f)self.append_log(f"✓ 成功配置已保存到:{self.success_config_file}")except Exception as e:self.append_log(f"保存成功配置失败:{str(e)}")def main():root = tk.Tk()app = FRPCClientGUI(root)root.protocol("WM_DELETE_WINDOW", lambda: on_closing(root, app))root.mainloop()def on_closing(root, app):"""窗口关闭时的处理"""if app.is_running:confirm = messagebox.askyesno("确认", "FRPC 正在运行,确定要退出程序吗?")if confirm:app.stop_frpc()app.save_config() # 保存配置root.destroy()else:app.save_config() # 保存配置root.destroy()if __name__ == "__main__":main()
然后是配置文件frpc_manager.ini(这个是给python程序加载使用了,最终会注入环境变量后用来执行frpc程序)
[FRP]server_addr = xxx.xxx.xxx.xxxserver_port = 7000token = xxxxxxxxxxremote_port = 7999name = rdp_3389local_ip = 127.0.0.1local_port = 3389user = test1 [UI]show_config = 1另外需要创建frpc文件夹,并放入frpc程序和配置文件frpc.env.toml(使用的是环境变量方式)
serverAddr = "{{ .Envs.FRP_SERVER_ADDR }}"serverPort = {{ .Envs.FRP_SERVER_PORT }}auth.token = "{{ .Envs.FRP_TOKEN }}"user = "{{ .Envs.FRP_USER }}" [[proxies]]name = "{{ .Envs.FRP_NAME }}"type = "tcp" localIP = "{{ .Envs.LOCAL_IP }}"localPort = {{ .Envs.LOCAL_PORT }}remotePort = {{ .Envs.FRP_REMOTE_PORT }}
上面准备好就可以python frpc_manager_gui.py启动啦(注意导入的一些包需要手动pip install安装下)
最后再附上操作说明说书吧(不用意外,这个也是让AI帮我生成的),如果你想继续按自己的需求修改这个程序的话,让AI先去读下这个文档,然后再把你的需求告诉它,让它继续开发功能吧!
# FRP 客户端管理器 - 软件操作说明书## 一、软件简介FRP 客户端管理器是一款基于 Python + Tkinter 开发的图形化管理工具,用于方便地配置和管理 FRP(Fast Reverse Proxy)客户端程序。该软件通过图形界面配置参数,自动注入环境变量启动 frpc,并实时监控运行状态和日志输出。### 主要功能- ✅ 图形化配置 FRP 参数,无需手动编辑配置文件- ✅ 一键启动/停止 FRPC 客户端- ✅ 实时显示 FRPC 控制台日志- ✅ 智能判断启动状态(成功/失败)- ✅ 配置参数持久化保存(INI 文件)- ✅ 程序启动时自动加载配置并启动 FRPC- ✅ Token 密码化显示,保护敏感信息- ✅ 动态显示端口转发规则说明---## 二、系统要求- **操作系统**:Windows 7/8/10/11- **Python 版本**:Python 3.6+- **依赖库**:Tkinter(Python 内置)- **FRP 客户端**:frpc.exe 及 frpc.env.toml 配置文件---## 三、安装与部署### 3.1 文件结构```frpc_client/├── frpc_manager_gui.py # 主程序├── frpc_manager.ini # 配置文件(首次运行自动生成)├── frpc_last_success.ini # 最后一次成功配置(启动成功后生成)└── frpc/├── frpc.exe # FRP 客户端程序└── frpc.env.toml # FRP 配置文件模板```### 3.2 运行方式直接双击运行或在命令行执行:```bashpython frpc_manager_gui.py```---## 四、界面说明### 4.1 配置参数区域配置区域共 5 行,包含 8 个配置项:**第 1 行:**- **FRP 服务器地址**:FRP 服务器的 IP 地址或域名(必填)- **FRP 服务器端口**:FRP 服务器的监听端口(必填,默认 7000)**第 2 行:**- **FRP Token**:FRP 认证令牌,以星号 `*` 隐藏显示(必填)- **FRP 代理名称**:自定义代理标识名称(必填)**第 3 行:**- **局域网主机 IP**:本地要映射的服务 IP(必填,如 127.0.0.1)- **局域网主机端口**:本地要映射的服务端口(必填,如 3389)**第 4 行:**- **FRP 远程端口**:FRP 服务器端对外开放的端口(必填)- **FRP 用户**:可选的用户标识(可选)**第 5 行:**- **配置说明**:动态显示端口转发规则```访问【FRP 服务器地址:FRP 远程端口】> 将会转发到本机可访问的地址 > 【局域网主机 IP:局域网主机端口】```### 4.2 控制按钮- **▶ 启动 FRPC**:启动 FRP 客户端- **◼ 停止 FRPC**:停止 FRP 客户端- **清空日志**:清除控制台输出日志### 4.3 状态指示器- **状态:已停止**(红色)- FRPC 未运行- **状态:运行中**(绿色)- FRPC 正常启动- **状态:启动失败**(红色)- 启动失败,自动停止进程### 4.4 日志显示区域黑色背景、绿色文字的仿终端样式,实时显示:- 启动配置信息- FRPC 运行日志- 启动成功/失败提示- 进程 ID 等信息---## 五、详细操作步骤### 5.1 首次使用1. **启动程序**- 双击运行 `frpc_manager_gui.py`- 程序自动创建空配置文件 `frpc_manager.ini`- 弹出提示框提示填写配置2. **配置参数**- 在配置区域填写所有必填字段- Token 输入时显示为星号,保护隐私- 观察底部的配置说明,确认转发规则正确3. **启动 FRPC**- 点击"▶ 启动 FRPC"按钮- 程序自动保存配置到 `frpc_manager.ini`- 日志区域显示启动过程4. **查看状态**- 启动成功:状态变为"已启动"(绿色),显示"✓ FRPC 启动成功!"- 启动失败:状态变为"启动失败"(红色),自动停止进程并提示原因### 5.2 日常使用#### 方式一:使用已有配置1. 打开程序(自动加载上次配置)2. 等待 500ms 后自动启动 FRPC3. 查看日志确认启动成功#### 方式二:修改配置后启动1. 打开程序2. 修改需要调整的参数3. 点击"▶ 启动 FRPC"4. 新配置会自动保存到 INI 文件#### 方式三:隐藏配置区域(简洁模式)1. 编辑 `frpc_manager.ini`2. 设置 `show_config = 0`3. 重启程序,只显示按钮和日志4. 配置通过 INI 文件预先设置### 5.3 停止 FRPC1. 点击"◼ 停止 FRPC"按钮2. 确认对话框点击"是"3. 程序安全终止 FRPC 进程4. 状态恢复为"已停止"### 5.4 退出程序1. 点击窗口关闭按钮2. 如果 FRPC 正在运行,会弹出确认框3. 确认后自动停止 FRPC 并保存配置4. 程序退出---## 六、配置文件说明### 6.1 主配置文件(frpc_manager.ini)```ini[FRP]server_addr = xxx.xxx.xxx.xxxserver_port = 7000token = xxxxxxxremote_port = xxxname = rdp_3389local_ip = 127.0.0.1local_port = 3389user = test1[UI]# 是否在主程序显示配置参数区域(1=显示,0=隐藏)- 手动修改此值show_config = 1```**配置项说明:**- `server_addr`:FRP 服务器地址(必填)- `server_port`:FRP 服务器端口(必填)- `token`:认证令牌(必填)- `remote_port`:远程映射端口(必填)- `name`:代理名称(必填)- `local_ip`:本地服务 IP(必填)- `local_port`:本地服务端口(必填)- `user`:用户标识(可选)- `show_config`:是否显示配置区域(1=显示,0=隐藏)### 6.2 成功配置文件(frpc_last_success.ini)- **用途**:记录最后一次成功启动的配置- **更新时机**:仅当启动成功时更新- **格式**:与主配置文件相同- **特点**:永不记录失败的配置### 6.3 FRP 配置文件(frpc/frpc.env.toml)这是 FRP 客户端的原始配置文件,使用环境变量占位符:```tomlserverAddr = "{{ .Envs.FRP_SERVER_ADDR }}"serverPort = {{ .Envs.FRP_SERVER_PORT }}auth.token = "{{ .Envs.FRP_TOKEN }}"user = "{{ .Envs.FRP_USER }}"[[proxies]]name = "{{ .Envs.FRP_NAME }}"type = "tcp"localIP = "{{ .Envs.LOCAL_IP }}"localPort = {{ .Envs.LOCAL_PORT }}remotePort = {{ .Envs.FRP_REMOTE_PORT }}```---## 七、高级功能### 7.1 自动启动程序启动时会自动检查配置文件:- ✓ 配置文件存在- ✓ 所有必填字段都有值- ✓ 满足条件则自动启动 FRPC- ✓ 延迟 500ms 执行,确保界面完全加载### 7.2 启动状态智能判断程序实时监控 FRPC 控制台输出:**成功检测:**- 检测到关键词:`start proxy success`- 状态变更为"已启动"(绿色)- 保存配置到 `frpc_last_success.ini`- 日志显示:✓ FRPC 启动成功!**失败检测:**- 检测到关键词:`already exists` / `error`- 状态变更为"启动失败"(红色)- 1 秒后自动停止进程- 日志显示:✗ FRPC 启动失败:代理可能已存在/网络异常/鉴权失败### 7.3 配置显示/隐藏切换**显示配置区域(适合初次配置):**```ini[UI]show_config = 1```**隐藏配置区域(适合日常使用):**```ini[UI]show_config = 0```**注意事项:**- ⚠️ 此配置只能手动在 INI 文件中修改- ⚠️ 程序不会自动更改此值- ⚠️ 默认值为 0(不显示)- ⚠️ 新建配置文件时默认为 0---## 八、常见问题### Q1: 启动失败提示"代理已存在"**原因**:FRP 服务器上该代理名称已被占用**解决**:1. 修改"FRP 代理名称"为其他名称2. 或在 FRP 服务器端删除旧配置### Q2: 启动失败提示"网络异常"或"鉴权失败"**原因**:服务器地址、端口或 Token 错误**解决**:1. 检查 FRP 服务器地址和端口是否正确2. 检查 Token 是否与服务器配置一致3. 确认 FRP 服务器正常运行### Q3: 配置区域不显示**原因**:`show_config` 设置为 0**解决**:1. 用文本编辑器打开 `frpc_manager.ini`2. 设置 `show_config = 1`3. 重启程序### Q4: 如何修改已保存的配置?**方法一**:直接在界面修改后点击启动**方法二**:用文本编辑器打开 `frpc_manager.ini` 直接修改### Q5: 在哪里查看 FRPC 的详细日志?- 程序界面的日志区域会实时显示 FRPC 输出- 支持自动滚动和清空功能- 黑色背景绿色文字,仿终端样式---## 九、环境变量映射表| 界面配置项 | 环境变量名 | TOML 配置对应 ||-----------|-----------|--------------|| FRP 服务器地址 | `FRP_SERVER_ADDR` | `serverAddr` || FRP 服务器端口 | `FRP_SERVER_PORT` | `serverPort` || FRP Token | `FRP_TOKEN` | `auth.token` || FRP 代理名称 | `FRP_NAME` | `proxies.name` || 局域网主机 IP | `LOCAL_IP` | `localIP` || 局域网主机端口 | `LOCAL_PORT` | `localPort` || FRP 远程端口 | `FRP_REMOTE_PORT` | `remotePort` || FRP 用户 | `FRP_USER` | `user` |---## 十、技术规格### 10.1 开发技术- **编程语言**:Python 3- **GUI 框架**:Tkinter- **配置文件**:INI 格式(configparser 模块)- **进程管理**:subprocess 模块### 10.2 核心特性- ✅ 多线程读取 FRPC 输出,避免界面卡顿- ✅ 使用 `root.after()` 安全更新 UI- ✅ 进程终止超时处理(5 秒后强制结束)- ✅ 配置文件 UTF-8 编码,支持中文注释- ✅ 异常捕获和用户友好的错误提示### 10.3 安全特性- Token 密码化显示(星号遮挡)- 配置文件不主动修改 UI 开关设置- 窗口关闭前确认和配置保存- 启动失败自动清理进程---## 十一、更新日志### v1.0(初始版本)- 基础 GUI 界面和 FRPC 启停功能- 环境变量注入机制- 实时日志显示### v1.1(配置优化)- 增加 INI 配置文件支持- 配置持久化和自动加载- 程序启动自动触发启动### v1.2(状态监控)- 智能判断启动状态- 启动失败自动停止- 成功配置备份功能### v1.3(界面优化)- 紧凑布局(每行 2 个配置项)- 配置区域显示/隐藏开关- Token 密码化显示- 动态配置说明### v1.4(当前版本)- 新增 FRP 服务器端口配置- 优化配置参数排列顺序- 增强配置说明文字---## 十二、联系与支持如有问题或建议,请参考:1. 本操作说明书2. FRP 官方文档3. 查看程序日志中的错误信息---**最后更新**:2026 年 3 月 12 日**软件版本**:v1.4**适用系统**:Windows
发表评论