ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

TaoToken 实战:Trae 在 Win10 下的端口占用检测工具(Python+wxPython)

2026/10/1 6:58:28 拓冰建站 浏览量
TaoToken 实战:Trae 在 Win10 下的端口占用检测工具(Python+wxPython) 1. 为什么 Win10 下还需要一个端口占用检测工具在 Win10 上跑本地服务最让人抓狂的场景之一就是启动项目时控制台甩出一句Address already in use或者10048 端口已被占用然后你就得切到命令行敲netstat -ano | findstr :8080从一堆输出里找到那个 PID再taskkill /F /PID xxxx。命令本身不难但每次都要重复这套动作尤其是同时开好几个微服务、前端 dev server、数据库端口的时候效率非常低。Linux 下有lsof -i:端口号一条命令直接定位Win10 原生没有这么顺手的等价物。netstat能查但输出格式对人不友好taskkill又要手动复制 PID。对于刚接触 Windows 命令行的小白来说这一套组合拳的门槛其实不低。我试过在 Trae 里用 Python wxPython 做一个带界面的端口占用检测工具核心诉求就三个输入端口号就能扫、扫描结果直接列出占用进程的 PID 和程序名、选中一行就能一键终止。Trae 作为编辑器负责代码补全和调试Python 负责逻辑wxPython 负责界面nuitka 负责打包成 exe 发给同事直接用。这篇文章会给出完整的依赖清单、可复制的核心检测代码、wxPython 界面配置以及运行验证步骤。适合谁在 Win10 上做本地开发、经常被端口冲突卡住、想用 Python 写个小工具但不想从零搭界面的同学。读完你能直接复现一个port_monitor.exe双击就能用。需要说明的是这个工具只做本地端口扫描和进程管理不涉及任何网络穿透或远程操作。如果你在团队里需要统一管理 API Key 和模型调用可以顺带了解一下 TaoToken 的接入方式后面会给出配置片段。2. 前置准备Trae 环境、Python 依赖与 TaoToken 接入配置2.1 Trae 在 Win10 下的基础配置Trae 是字节做的 AI 编辑器Win10 下安装包直接双击下一步就行。装完后建议做两件事一是把 Python 解释器路径配好二是打开终端面板方便跑命令。Trae 的 AI 补全对 wxPython 这种 GUI 库挺友好你写wx.Frame的时候它会提示构造函数参数省去查文档的时间。Python 版本建议 3.9 到 3.11太新的版本某些 wxPython wheel 可能还没跟上。装完 Python 后确认python --version和pip --version都能正常输出。2.2 依赖清单这个工具只依赖两个东西wxPython 和 nuitka。wxPython 负责界面nuitka 负责打包。pip 安装命令如下pip install wxPython pip install nuitkawxPython 在 Win10 下安装时如果遇到编译错误通常是因为缺少 Visual C 构建工具。最省事的办法是去 wxPython 官网下载对应 Python 版本的 whl 文件然后pip install 文件名.whl本地安装。nuitka 打包时需要 C 编译器Win10 下推荐装 Visual Studio Build Tools勾选「使用 C 的桌面开发」即可。2.3 TaoToken 接入配置可选但推荐如果你在开发过程中需要调用大模型 API 来做代码补全、日志分析或者自动化脚本TaoToken 提供了一个统一的接入入口。它的 Base URL 是https://taotoken.net/api你需要在控制台生成 API Key然后在代码或工具里配置。以 Claude Code 或类似支持 Anthropic 协议的工具为例配置文件通常长这样{ base_url: https://taotoken.net/api, api_key: sk-你的Key, model: claude-sonnet-4-20250514 }如果你用的是 Cline、CC Switch 这类工具配置项一般拆成三件套Base URL 填https://taotoken.net/apiAPI Key 填控制台生成的 KeyModel ID 填你要用的模型名。Codex 的auth.json也是类似结构把 base URL 和 key 填进去就行。需要提醒的是TaoToken 只是模型调用的接入层不替代编辑器本身。Trae 负责写代码TaoToken 负责在你需要模型能力时提供 API。两者配合使用开发体验会更顺。控制台地址和 API Key 管理页面可以通过这个入口进入https://taotoken.net/api-keys 模型对话调试可以用 https://taotoken.net/model-chat 接入文档在 https://taotoken.net/doc 。如果你长期做编码和 Agent 类任务可以看看 Coding Planhttps://taotoken.net/coding-plan 。3. 可复制配置wxPython 界面与端口检测核心代码3.1 项目结构在 Trae 里新建一个文件夹比如port_monitor里面放三个文件port_monitor/ ├── port_monitor.py # 主程序 ├── build.bat # nuitka 打包脚本 └── requirements.txt # 依赖清单requirements.txt内容wxPython4.2.0 nuitka1.83.2 主程序 port_monitor.py下面是完整的核心代码。它做了几件事用 wxPython 搭一个窗口输入端口号后开线程跑netstat -ano解析出占用该端口的 PID再用tasklist和wmic拿到程序名和路径最后把结果填到 ListCtrl 里。选中某一行点「终止占用进程」就执行taskkill。import wx import subprocess import re import threading class PortMonitorFrame(wx.Frame): def __init__(self, parent, title): super(PortMonitorFrame, self).__init__(parent, titletitle, size(760, 460)) self.InitUI() self.Centre() self.is_scanning False def InitUI(self): panel wx.Panel(self) vbox wx.BoxSizer(wx.VERTICAL) hbox1 wx.BoxSizer(wx.HORIZONTAL) self.port_input wx.TextCtrl(panel) 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) self.result_list.InsertColumn(0, PID, width90) self.result_list.InsertColumn(1, 程序名称, width180) self.result_list.InsertColumn(2, 状态, width120) self.result_list.InsertColumn(3, 是否监听, width90) self.result_list.InsertColumn(4, 程序路径, width260) vbox.Add(hbox1, flagwx.EXPAND | wx.LEFT | wx.RIGHT | wx.TOP, border10) vbox.Add(hbox2, flagwx.EXPAND | wx.ALL, border10) vbox.Add(self.result_list, proportion1, flagwx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM, border10) panel.SetSizer(vbox) def _startupinfo(self): si subprocess.STARTUPINFO() si.dwFlags | subprocess.STARTF_USESHOWWINDOW return si 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) def scan_task(): try: output subprocess.check_output( [netstat, -ano], textTrue, stderrsubprocess.STDOUT, startupinfoself._startupinfo() ) pattern re.compile(rTCP\s.*?:{}\s.*?\s(\d).format(port)) pids pattern.findall(output) if not pids: wx.CallAfter(wx.MessageBox, 端口未被占用, 提示, wx.ICON_INFORMATION) return for pid in set(pids): if not self.is_scanning: break task_info subprocess.check_output( [tasklist, /fi, fpid eq {pid}], textTrue, stderrsubprocess.STDOUT, startupinfoself._startupinfo() ) proc_match re.search(r(.?)\s\d\sConsole, task_info) proc_name proc_match.group(1).strip() if proc_match else 未知程序 detail re.compile(rTCP\s.*?:{}\s.*?\s(\S)\s({}).format(port, pid)) status_match detail.search(output) is_listening status_match.group(1) LISTENING if status_match else False try: path_out subprocess.check_output( [wmic, process, where, fprocessid{pid}, get, executablepath], textTrue, stderrsubprocess.STDOUT, startupinfoself._startupinfo() ) path_match re.search(rExecutablePath\s(.?)\s*$, path_out, re.MULTILINE) proc_path path_match.group(1).strip() if path_match else 未知路径 except Exception: proc_path 未知路径 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) threading.Thread(targetscan_task, daemonTrue).start() def _update_ui(self, pid, proc_name, is_listening, proc_path): index self.result_list.InsertItem(self.result_list.GetItemCount(), pid) self.result_list.SetItem(index, 1, proc_name) self.result_list.SetItem(index, 2, 运行中) self.result_list.SetItem(index, 3, 是 if is_listening else ) self.result_list.SetItem(index, 4, proc_path) if is_listening: self.result_list.SetItemTextColour(index, wx.RED) 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: subprocess.check_output( [taskkill, /F, /PID, pid], textTrue, stderrsubprocess.STDOUT, startupinfoself._startupinfo() ) 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()几个关键点解释一下。subprocess.STARTUPINFO配合STARTF_USESHOWWINDOW是为了在调用netstat、tasklist、wmic时不弹出黑色命令行窗口否则每次扫描都会闪一下。wx.CallAfter是因为扫描跑在子线程里更新 UI 必须回到主线程否则 wxPython 会报错。is_listening判断的是连接状态是否为LISTENING是的话那一行标红方便你一眼看出哪个进程在监听。3.3 打包脚本 build.batnuitka 打包比 pyinstaller 干净不会在用户目录留缓存。在项目根目录建一个build.batecho off nuitka --standalone --onefile --windows-console-modedisable port_monitor.py echo 打包完成可执行文件在 dist 目录下 pause--standalone表示独立打包--onefile表示合成单个 exe--windows-console-modedisable表示运行时不显示控制台窗口。双击build.bat等几分钟dist目录下就会出现port_monitor.exe。4. 运行验证从 python 脚本到 exe 的完整测试4.1 直接跑 Python 脚本在 Trae 的终端里执行python port_monitor.py窗口出来后输入一个你确定被占用的端口比如你本地起了个服务在 8080就输入8080点「检测端口占用」。正常情况下 ListCtrl 里会出现一行或多行记录PID、程序名称、状态、是否监听、程序路径都有。如果该端口是 LISTENING 状态那一行会标红。如果输入一个没被占用的端口比如59999会弹窗提示「端口未被占用」。4.2 测试终止进程选中一行点「终止占用进程」。如果权限足够会弹「进程终止成功」同时那一行从列表里消失。如果进程属于系统服务或者需要管理员权限会弹「终止进程失败」这时候你需要以管理员身份重新运行工具。4.3 打包成 exe 再验证双击build.bat等它跑完。然后进dist目录双击port_monitor.exe。第一次启动可能会慢几秒因为 nuitka 打包的程序需要解压运行时。之后的操作和 Python 脚本完全一致。实测下来打包后的 exe 在 Win10 上可以直接发给同事不需要他们装 Python 和 wxPython。文件体积大概在 30 到 50 MB 之间取决于 wxPython 的版本。4.4 验证成功的结果一个完整的成功流程是这样的打开 exe输入3306假设你本地有 MySQL点检测列表出现mysqld.exe状态显示运行中是否监听显示「是」路径显示 MySQL 的 bin 目录。选中它点终止弹窗成功列表清空。再点一次检测提示端口未被占用。如果你在开发过程中需要调用模型 API 来做一些自动化处理比如把扫描结果发给模型分析可以在代码里加一段 HTTP 请求Base URL 用https://taotoken.net/apiKey 从控制台拿。模型对话调试入口在 https://taotoken.net/model-chat 接入文档在 https://taotoken.net/doc 。5. 常见报错排查401、local proxy failed、reading choices 与 OAuth5.1 401 错误如果你在工具里配置了 TaoToken 的 API Key但请求返回 401通常是 Key 没填对或者没带上。检查三件套Base URL 是不是https://taotoken.net/apiKey 是不是从 https://taotoken.net/api-keys 生成的Model ID 是不是写对了。有时候复制 Key 会多带空格用strip()处理一下。5.2 local proxy failed这个报错一般出现在你本地配了代理但代理没启动或者端口不对。Win10 下检查「设置 - 网络和 Internet - 代理」确认没有开启手动代理或者代理地址和端口是正确的。如果你在用 Cline、CC Switch 这类工具检查它们的网络配置里有没有指向一个不存在的本地端口。5.3 reading choices 报错这个通常出现在调用模型 API 时返回的 JSON 结构里没有choices字段。原因可能是 Base URL 填错了比如填成了https://taotoken.net而不是https://taotoken.net/api导致请求打到了网页而不是 API 端点。确认 URL 路径完整。5.4 OAuth 相关报错如果你用的是 Claude Code 或者类似需要 OAuth 的工具报 OAuth 错误通常是 token 过期或者回调地址不对。检查auth.json或配置文件里的 token 是否还有效必要时重新生成。Codex 的auth.json里 base URL 和 key 要对应上不要混用不同环境的配置。5.5 端口扫描本身的问题如果netstat -ano返回空或者解析不到 PID可能是权限不够。Win10 下普通用户跑netstat一般没问题但wmic查进程路径可能需要管理员权限。解决办法是右键 exe 选择「以管理员身份运行」。另外如果端口号输入了非数字代码里会弹「请输入有效的端口号」这是预期行为。5.6 nuitka 打包报错如果build.bat跑的时候报缺少 C 编译器装 Visual Studio Build Tools勾选「使用 C 的桌面开发」。如果报 wxPython 相关模块找不到试试先pip install --upgrade wxPython或者用--include-packagewx参数显式包含。6. 从工具到工作流把端口检测接入你的日常开发这个工具本身不复杂但它的价值在于把「查端口 - 找 PID - 杀进程」这三步压缩成一次点击。你可以把它固定在任务栏遇到端口冲突时直接点开输入端口号两秒内看到结果。如果你想让它在团队里更好用可以加几个小功能一是支持多个端口批量扫描用逗号分隔二是把扫描历史存到本地 SQLite方便回溯三是加一个「复制 PID」按钮方便你在其他工具里用。这些改动在 Trae 里让 AI 补全帮你写效率很高。对于需要调用模型能力的场景比如自动分析端口占用日志、生成释放建议可以在工具里加一个「AI 分析」按钮把扫描结果 POST 到https://taotoken.net/api的对话端点让模型给出处理建议。配置还是那三件套Base URL、API Key、Model ID。控制台在 https://taotoken.net/console API Key 管理在 https://taotoken.net/api-keys 文档在 https://taotoken.net/doc 。如果你长期做编码和 Agent 任务Coding Plan 的入口在 https://taotoken.net/coding-plan 。最后说一个实际踩过的坑nuitka 打包时如果用了--onefileexe 启动会比--standalone慢因为每次都要解压。如果你追求启动速度可以去掉--onefile直接发整个dist文件夹。另外wxPython 的界面在高分屏上可能有点糊可以在wx.App初始化前加一行 DPI 感知设置具体代码在 wxPython 文档里有。