|
1 | 1 | import os |
2 | 2 | import sys |
| 3 | +import asyncio |
3 | 4 | import subprocess |
4 | 5 | os.environ["PYTHONFAULTHANDLER"] = "0" |
5 | 6 | if sys.platform == "win32": |
6 | 7 | try: |
7 | 8 | import win32api |
8 | 9 | win32api.SetConsoleCtrlHandler(None, 0) |
9 | 10 | except:pass |
| 11 | + |
| 12 | + # Windows 下静默处理底层 socket 异常(如网络切换导致的 WinError 64), |
| 13 | + # 避免 asyncio 回调把已断开连接的错误刷成 traceback |
| 14 | + def _loop_exception_handler(loop, context): |
| 15 | + exc = context.get("exception") |
| 16 | + msg = str(context.get("message", "")) |
| 17 | + if isinstance(exc, OSError) or "recvfrom" in msg or "WinError 64" in msg: |
| 18 | + return |
| 19 | + loop.default_exception_handler(context) |
| 20 | + try: |
| 21 | + asyncio.get_event_loop().set_exception_handler(_loop_exception_handler) |
| 22 | + except Exception: |
| 23 | + pass |
10 | 24 | from fastapi import FastAPI, Request, HTTPException |
11 | 25 | from fastapi.middleware.cors import CORSMiddleware |
12 | 26 | from fastapi.responses import HTMLResponse, JSONResponse, FileResponse |
@@ -172,5 +186,14 @@ def start_monitor(): |
172 | 186 | start_monitor() |
173 | 187 | try: |
174 | 188 | import uvicorn |
175 | | - uvicorn.run(app, host=HOST, port=PORT) |
| 189 | + # Windows 下用 wsproto 处理 WebSocket:基于纯 asyncio 流,规避 ProactorEventLoop |
| 190 | + # 在 UDP/传输 socket 上的 WinError 64 异常(默认 websockets 库会冒泡到 asyncio 回调) |
| 191 | + run_kwargs = {} |
| 192 | + if sys.platform == "win32": |
| 193 | + try: |
| 194 | + import wsproto # noqa: F401 |
| 195 | + run_kwargs["ws"] = "wsproto" |
| 196 | + except Exception: |
| 197 | + pass |
| 198 | + uvicorn.run(app, host=HOST, port=PORT, **run_kwargs) |
176 | 199 | finally:shutdown_nvml() |
0 commit comments