尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

用 Trae 开发 Win10 端口占用检测工具:TaoToken 统一 Key 配置与 wxPython 界面骨架

用 Trae 开发 Win10 端口占用检测工具:TaoToken 统一 Key 配置与 wxPython 界面骨架 1. Win10 端口占用检测工具为什么我要用 Trae 重写一遍在 Win10 上跑 Python 项目最烦的场景之一就是启动服务时弹出OSError: [WinError 10048] 通常每个套接字地址只允许使用一次翻译成人话就是端口被占了。Linux 下有lsof -i:端口号一行命令搞定Windows 下你得先netstat -ano | findstr 8080找到 PID再tasklist /fi pid eq 12345查进程名最后taskkill /F /PID 12345干掉它三步下来命令行敲得手酸。我这次用 Trae 配合 Python wxPython 做了一个带界面的端口占用检测工具输入端口号点一下就能看到占用进程的 PID、程序名、状态和可执行路径选中后一键终止。但真正让我想写这篇文章的不是界面本身而是开发过程中踩到的一个坑Trae 里同时挂了 Cline、CC Switch 好几个 AI 编码插件每个都要填 API Key、Base URL、模型名配置散落在不同插件的 settings.json 里改一次要翻四五个文件。后来我把它们统一收敛到 TaoToken 的一套 Key 和 API 通道上config.toml 写一份各插件引用同一份配置才算清爽。这篇内容适合两类人一是想在 Win10 上快速搭一个端口检测小工具的 Python 新手二是同时用多个 AI 编码工具、被 Key 管理搞烦的开发者。下面我会先给 TaoToken 的统一配置骨架再给完整的 wxPython 界面代码和 Nuitka 打包脚本最后演示一次真实的端口扫描验证。2. TaoToken 前置一套 Key 打通 Trae 里的多个 AI 工具Trae 本身是字节出的 AI IDE内置了模型对话能力但很多人会额外装 Cline 做 Agent 式编码、装 CC Switch 切换不同的模型通道。问题在于每个插件都有自己的配置入口Key 填重复了不说换模型时还得逐个改。TaoToken 在这里的角色是一个统一的 API 通道你只需要在官网注册后拿到一个 Key然后在各个工具里把 Base URL 指向https://taotoken.net/api模型名按需填就能共用同一套凭证。这样做的好处是Trae 内置对话、Cline、CC Switch 走的是同一个出口额度、日志、模型切换都在一处管理不用再记五六个不同的 Key。具体操作路径我列一下你按需点注册和拿 Key进官网 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 登录后在控制台创建 API Key。控制台管理https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content创建 Key 页面https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content模型对话体验https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content接入文档https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content注意API 地址统一用https://taotoken.net/api不要在后面拼多余的路径OpenAI 兼容协议下客户端会自动补/v1/chat/completions。如果你主要是长期跑编码 Agent比如让 Cline 连续改代码、跑测试建议看一下 Coding Plan额度模型更适合高频调用https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content3. 可复制配置config.toml 与 settings.json 骨架我习惯把公共配置抽到一个config.toml里放在项目根目录然后各个插件的settings.json引用它。这样换 Key 或换模型只改一处。3.1 config.toml 统一配置骨架# config.toml —— 放在项目根目录供各 AI 插件读取 [taotoken] base_url https://taotoken.net/api api_key sk-你的TaoToken密钥 default_model claude-sonnet-4-20250514 timeout 60 [taotoken.models] chat claude-sonnet-4-20250514 coding claude-sonnet-4-20250514 fast gpt-4o-mini [port_monitor] default_port 8080 scan_timeout 10Python 侧读取用标准库tomllibPython 3.11或tomliimport tomllib with open(config.toml, rb) as f: cfg tomllib.load(f) base_url cfg[taotoken][base_url] api_key cfg[taotoken][api_key]3.2 Cline 的 settings.json 接入Cline 在 VS Code / Trae 里的配置存在扩展设置里也可以直接改settings.json。关键字段是 API Provider 选 OpenAI Compatible然后填 Base URL 和 Key{ cline.apiProvider: openai, cline.openAiBaseUrl: https://taotoken.net/api, cline.openAiApiKey: sk-你的TaoToken密钥, cline.openAiModelId: claude-sonnet-4-20250514, cline.customInstructions: 回答用中文代码注释用中文 }3.3 CC Switch 的 settings.json 接入CC Switch 用来在多个模型通道间切换配置结构类似重点是baseUrl和apiKey指向同一套{ providers: [ { name: taotoken, baseUrl: https://taotoken.net/api, apiKey: sk-你的TaoToken密钥, models: [claude-sonnet-4-20250514, gpt-4o-mini], active: true } ] }提示两个插件都指向同一个 Base URL 和 Key 后你在 TaoToken 控制台看到的调用日志就是合并的排查哪个插件在乱调模型会方便很多。4. wxPython 界面骨架与端口扫描核心逻辑配置搞定后回到工具本身。整个程序分三块界面布局、扫描线程、进程终止。我把它拆开讲方便你对照改。4.1 界面布局输入框 两个按钮 结果列表主窗口用wx.Frame里面放一个垂直 BoxSizer顶部是端口输入框中间是两个按钮底部是wx.ListCtrl表格列分别是 PID、程序名称、状态、是否为主程序、程序路径。import wx import subprocess import re import threading class PortMonitorFrame(wx.Frame): def __init__(self, parent, title): super().__init__(parent, titletitle, size(760, 460)) self.is_scanning False self.InitUI() self.Centre() def InitUI(self): panel wx.Panel(self) vbox wx.BoxSizer(wx.VERTICAL) hbox1 wx.BoxSizer(wx.HORIZONTAL) self.port_input wx.TextCtrl(panel, value8080) hbox1.Add(wx.StaticText(panel, label端口号), flagwx.RIGHT | wx.ALIGN_CENTER_VERTICAL, border8) hbox1.Add(self.port_input, proportion1) hbox2 wx.BoxSizer(wx.HORIZONTAL) self.check_btn wx.Button(panel, label检测端口占用) self.kill_btn wx.Button(panel, label终止占用进程) self.check_btn.Bind(wx.EVT_BUTTON, self.on_check_port) self.kill_btn.Bind(wx.EVT_BUTTON, self.on_kill_process) hbox2.Add(self.check_btn, flagwx.RIGHT, border8) hbox2.Add(self.kill_btn) self.result_list wx.ListCtrl(panel, stylewx.LC_REPORT) for i, (col, w) in enumerate([(PID, 90), (程序名称, 180), (状态, 110), (是否为主程序, 110), (程序路径, 260)]): self.result_list.InsertColumn(i, col, widthw) vbox.Add(hbox1, flagwx.EXPAND | wx.ALL, border10) vbox.Add(hbox2, flagwx.EXPAND | wx.LEFT | wx.RIGHT, border10) vbox.Add(self.result_list, proportion1, flagwx.EXPAND | wx.ALL, border10) panel.SetSizer(vbox)4.2 扫描线程netstat 解析 tasklist 查进程名扫描必须放子线程否则界面会卡死。核心是调netstat -ano拿全部连接用正则匹配目标端口再对每个 PID 调tasklist拿进程名调wmic拿可执行路径。def on_check_port(self, event): port self.port_input.GetValue().strip() if not port.isdigit(): wx.MessageBox(请输入有效的端口号, 错误, wx.ICON_ERROR) return if self.is_scanning: wx.MessageBox(扫描正在进行中, 提示, wx.ICON_INFORMATION) return self.result_list.DeleteAllItems() self.is_scanning True self.check_btn.Enable(False) threading.Thread(targetself.scan_task, args(port,), daemonTrue).start() def scan_task(self, port): try: si subprocess.STARTUPINFO() si.dwFlags | subprocess.STARTF_USESHOWWINDOW output subprocess.check_output( [netstat, -ano], textTrue, stderrsubprocess.STDOUT, startupinfosi ) pattern re.compile(rTCP\s.*?:{}\s.*?\s(\d).format(port)) pids set(pattern.findall(output)) if not pids: wx.CallAfter(wx.MessageBox, 端口未被占用, 提示, wx.ICON_INFORMATION) return for pid in pids: task_info subprocess.check_output( [tasklist, /fi, fpid eq {pid}], textTrue, stderrsubprocess.STDOUT, startupinfosi ) m re.search(r(.?)\s\d\sConsole, task_info) proc_name m.group(1).strip() if m else 未知程序 is_listening LISTENING in output.split(pid)[0][-80:] proc_path self.get_proc_path(pid, si) wx.CallAfter(self.update_ui, pid, proc_name, is_listening, proc_path) except subprocess.CalledProcessError as e: wx.CallAfter(wx.MessageBox, f执行命令失败{e.output}, 错误, wx.ICON_ERROR) finally: self.is_scanning False wx.CallAfter(self.check_btn.Enable, True) def get_proc_path(self, pid, si): try: out subprocess.check_output( [wmic, process, where, fprocessid{pid}, get, executablepath], textTrue, stderrsubprocess.STDOUT, startupinfosi ) m re.search(rExecutablePath\s(.?)\s*$, out, re.MULTILINE) return m.group(1).strip() if m else 未知路径 except Exception: return 未知路径 def update_ui(self, pid, proc_name, is_listening, proc_path): idx self.result_list.InsertItem(self.result_list.GetItemCount(), pid) self.result_list.SetItem(idx, 1, proc_name) self.result_list.SetItem(idx, 2, 运行中) self.result_list.SetItem(idx, 3, 是 if is_listening else ) self.result_list.SetItem(idx, 4, proc_path) if is_listening: self.result_list.SetItemTextColour(idx, wx.RED)4.3 终止进程taskkill 一键清理选中表格某一行点终止按钮调taskkill /F /PID强制结束成功后从列表删掉那一行。def on_kill_process(self, event): if self.is_scanning: wx.MessageBox(扫描未完成请稍后, 提示, wx.ICON_INFORMATION) return selected self.result_list.GetFirstSelected() if selected -1: wx.MessageBox(请选择要终止的进程, 提示, wx.ICON_INFORMATION) return pid self.result_list.GetItemText(selected) try: si subprocess.STARTUPINFO() si.dwFlags | subprocess.STARTF_USESHOWWINDOW subprocess.check_output( [taskkill, /F, /PID, pid], textTrue, stderrsubprocess.STDOUT, startupinfosi ) self.result_list.DeleteItem(selected) wx.MessageBox(进程终止成功, 提示, wx.ICON_INFORMATION) except subprocess.CalledProcessError as e: wx.MessageBox(f终止进程失败{e.output}, 错误, wx.ICON_ERROR) if __name__ __main__: app wx.App() frame PortMonitorFrame(None, 端口监控工具) frame.Show() app.MainLoop()5. 验证请求跑一次真实端口扫描代码写完后先直接跑 Python 脚本验证pip install wxpython python port_monitor.py窗口出来后我故意先起一个占端口的服务来测python -m http.server 8080然后在工具里输入8080点「检测端口占用」。实测下来表格里会立刻出现一行PID 是 python 进程号程序名称python.exe状态「运行中」是否为主程序「是」路径指向你的 Python 安装目录整行标红。选中它点「终止占用进程」弹窗提示成功再点一次检测就显示「端口未被占用」。如果你还想验证 TaoToken 那套配置是否生效可以在 Trae 里打开 Cline让它读一下config.toml并解释字段含义。如果 Cline 能正常返回中文解释说明 Base URL 和 Key 都通了。想单独测模型通道用模型对话页面发一条消息最快https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content5.1 Nuitka 打包成 exe确认脚本能跑后用 Nuitka 编译成单文件 exe。相比 PyInstallerNuitka 把 Python 编译成 C 再编译启动更快也不会在用户目录留一堆缓存。pip install nuitka写一个build.batecho off nuitka --standalone --onefile --windows-console-modedisable ^ --enable-pluginwx ^ --output-dirdist ^ port_monitor.py echo 打包完成可执行文件在 dist 目录下 pause注意这里加了--enable-pluginwxwxPython 有动态库依赖不加这个插件打包出来的 exe 运行时会报找不到 DLL。跑完build.batdist目录下就有port_monitor.exe双击直接运行不需要装 Python 环境。6. 本篇常见错排查报错一ModuleNotFoundError: No module named wx说明 wxPython 没装成功。Win10 上建议用pip install wxpython装预编译 wheel如果卡在编译检查 Python 版本是不是 3.12部分老版本 wxPython 对 3.12 支持滞后换 3.11 更稳。报错二打包后 exe 双击闪退九成是没加--enable-pluginwx或者--windows-console-modedisable把错误信息也吞了。排查时先改成--windows-console-modeforce重新打包看控制台报什么错。报错三netstat输出解析不到 PIDWin10 中文系统下netstat -ano的表头是中文但数据行格式不变正则匹配TCP\s.*?:端口\s.*?\s(\d)依然有效。如果匹配不到先手动跑一遍netstat -ano | findstr 8080看输出格式确认端口确实处于 LISTENING 或 ESTABLISHED。报错四taskkill提示「拒绝访问」目标进程可能是系统服务或需要管理员权限。右键 exe 选「以管理员身份运行」或者在代码里加 UAC 提权判断。普通用户进程一般不会遇到。报错五Cline 报 401 或 model not found先确认settings.json里 Base URL 是https://taotoken.net/api没有多余斜杠再确认 Key 没有前后空格。模型名要和控制台里可用的模型一致写错了会返回 model not found。接入细节可以对照文档https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content报错六扫描时界面卡死说明扫描逻辑跑在主线程了。检查on_check_port里是不是用threading.Thread起的子线程所有 UI 更新是不是都包在wx.CallAfter里。wxPython 不允许子线程直接操作控件。7. 把 Key 和工具都收进一套配置里这个端口检测工具本身不复杂真正省时间的是把 Trae 里多个 AI 插件的 Key 收敛到 TaoToken 一套通道上。config.toml 写一份Cline 和 CC Switch 的 settings.json 都指向https://taotoken.net/api以后换模型、查额度、看调用日志都在一个控制台里完成不用再翻四五个配置文件。如果你后面想让 Cline 帮你继续扩展这个工具比如加个「扫描全部监听端口」按钮、加导出 CSV 功能直接让它读config.toml和port_monitor.py就行模型通道已经通了。长期跑编码 Agent 的话Coding Plan 的额度模型更划算https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content最后留一个我踩过的坑Nuitka 打包时如果项目里引用了config.toml记得用--include-data-filesconfig.tomlconfig.toml把它一起打进去否则 exe 运行时读不到配置会直接抛FileNotFoundError。这个报错不会在编译阶段出现只有双击 exe 才暴露第一次打包很容易漏。
返回列表