5.Windows端口冲突10048彻底解决:查杀改防全流程
Windows 端口冲突 10048 彻底解决:查 / 杀 / 改 / 防全流程
适用场景:后端服务(uvicorn / FastAPI / Flask / Node 等)启动时报
OSError: [WinError 10048] 通常每个套接字地址(协议/网络地址/端口)只允许使用一次
本文以 uvicorn 占用 8000 端口为例,给出可复用的排查与根治方案。
0. 主要命令(TL;DR)
| 动作 | 命令 |
|---|---|
| 查谁占了 8000 | netstat -ano | findstr ":8000" |
| 按 PID 强杀 | taskkill /PID 40524 /F |
| 临时换端口 | uvicorn app.main:app --port 8001(前端代理也要改) |
| 根治:启动前自动查杀 | 写 start.ps1 脚本(见第五节) |
核心认知:10048 不是 bug,是"另一个进程已经绑了同一个端口"。解决思路永远是:找到它、干掉它、或绕开它。
一、为什么会出现 10048
报错原文:
OSError: [WinError 10048] 通常每个套接字地址(协议/网络地址/端口)只允许使用一次
含义:一个端口(如 8000)同一时刻只能被一个进程监听。你启动第二个 uvicorn 时,它尝试 bind(0.0.0.0:8000) 失败,直接退出。
常见触发原因:
- 上一次服务没正常退出(终端被关、IDE 没点 Stop、后台进程残留)
- 父进程退出但子进程/残留 socket 还占着端口(multiprocessing fork 场景)
- 同时开了两个终端 / IDE 都在跑后端
- Windows
TIME_WAIT半关闭状态(通常 30 秒内自动释放,较少见)
二、查:定位占用端口的进程
方法一:最常用(看 PID)
netstat -ano | findstr ":8000"
输出:
TCP 0.0.0.0:8000 0.0.0.0:0 LISTENING 40524
最后一列 40524 就是 PID(进程 ID)。
方法二:PowerShell 精确查
Get-NetTCPConnection -LocalPort 8000 -State Listen
OwningProcess 字段即 PID。再查进程名:
Get-Process -Id 40524
方法三:看命令行(确认是不是 uvicorn)
Get-CimInstance Win32_Process -Filter "ProcessId=40524" | Select-Object CommandLine
输出含 python.exe -m uvicorn app.main:app ... 即后端服务。
方法四:列出所有 python 进程
Get-Process -Name python | Select-Object Id, Path | Format-Table -AutoSize
三、杀:释放端口
按 PID 精准杀(推荐)
taskkill /PID 40524 /F
/F = 强制结束,不等待进程优雅退出。
按名称批量杀
taskkill /F /IM uvicorn.exe
⚠️ 若进程名是 python.exe(uvicorn 常以 python -m 方式运行):
taskkill /F /IM python.exe
警告:这会杀掉所有 python 进程,包括 IDE、其他项目、后台脚本。尽量先用 PID 精准杀。
杀不掉的特例:僵尸父进程 + 残留子进程
有时 netstat / Get-NetTCPConnection 显示 PID 是父进程(已退出),taskkill 报"找不到进程"。真正持有 socket 的是它 fork 出来的子进程。
查子进程:
Get-CimInstance Win32_Process -Filter "ParentProcessId=40524" | Select-Object ProcessId, CommandLine
杀子进程(它才是真正占用者):
taskkill /PID <子进程PID> /F
实战案例:某次
uv run python xxx启动后父进程退出,但 multiprocessing fork 出的子进程仍持有 8000,导致重启 uvicorn 一直 10048。杀掉那个子进程后端口才释放。
四、改:临时绕开(不想杀旧进程时)
1. 后端换端口
uv run uvicorn app.main:app --host 0.0.0.0 --port 8001
2. 前端代理同步改
frontend/vite.config.ts:
server: {
proxy: {
'/api': {
target: 'http://localhost:8001', // 改成新端口
changeOrigin: true,
},
},
},
改完重启前端 npm run dev。
3. 用环境变量控制端口(推荐)
backend/.env:
PORT=8000
代码读取:
import os
port = int(os.getenv("PORT", "8000"))
启动:uv run uvicorn app.main:app --host 0.0.0.0 --port $env:PORT
以后换端口只改 .env,不动代码。
五、防:写"先查杀再启动"脚本(根治)
把下面内容存为 backend/start.ps1,以后只用它启动后端,永远不再撞 10048。
# backend/start.ps1
$Port = 8000
Write-Host "Checking port $Port ..."
$listeners = Get-NetTCPConnection -LocalPort $Port -State Listen -ErrorAction SilentlyContinue
if ($listeners) {
$pids = $listeners.OwningProcess | Sort-Object -Unique
Write-Host "Port $Port occupied by PID(s): $([string]::Join(', ', $pids))"
foreach ($pid in $pids) {
$proc = Get-Process -Id $pid -ErrorAction SilentlyContinue
if ($proc) {
Write-Host "Killing PID $pid ($($proc.Name)) ..."
Stop-Process -Id $pid -Force -ErrorAction SilentlyContinue
}
}
Start-Sleep -Seconds 2
}
$still = Get-NetTCPConnection -LocalPort $Port -State Listen -ErrorAction SilentlyContinue
if ($still) {
Write-Host "WARNING: port $Port still occupied by $([string]::Join(', ', $still.OwningProcess))" -ForegroundColor Yellow
} else {
Write-Host "Port $Port is free, starting uvicorn ..." -ForegroundColor Green
}
uv run uvicorn app.main:app --host 0.0.0.0 --port $Port
使用:
cd backend
.\start.ps1
六、根治方案优先级
| 方案 | 效果 | 命令 |
|---|---|---|
| ① 启动脚本自动查杀 | 最推荐,一劳永逸 | .\start.ps1 |
| ② 换端口 | 临时绕开 | --port 8001 + 改前端代理 |
| ③ 关闭旧终端 / IDE 点 Stop | 行为层面根治 | Ctrl+C 或 Stop 按钮 |
| ④ 单一运行入口 | 避免多开 | 只用 IDE 一个 Run 配置 |
| ⑤ SO_REUSEADDR | 一般不需 | uvicorn 主进程不适用 |
七、完整排查清单(照着做)
# 1. 查
netstat -ano | findstr ":8000"
# 2. 杀
taskkill /PID <上面查到的PID> /F
# 3. 确认释放
netstat -ano | findstr ":8000" # 无输出即成功
# 4. 启动
uv run uvicorn app.main:app --host 0.0.0.0 --port 8000
八、一句话总结
- 查:
netstat -ano | findstr ":端口"找到 PID - 杀:
taskkill /PID <pid> /F强杀(杀不掉查子进程) - 改:
--port 8001绕开,前端代理同步改 - 防:用
start.ps1启动前自动查杀,彻底告别 10048
10048 不是玄学,是端口被占。找到它、干掉它、或绕开它,三选一即可。

浙公网安备 33010602011771号