diff --git a/lite/proxy_lite.py b/lite/proxy_lite.py index 3b930c6..08783dd 100644 --- a/lite/proxy_lite.py +++ b/lite/proxy_lite.py @@ -228,17 +228,15 @@ def _start_pac_server(pac_content: str) -> int: return port def _stop_pac_server(): - """停止本地 PAC HTTP 服务;shutdown() 可能阻塞,因此先关闭 socket 再异步收尾。""" + """停止本地 PAC HTTP 服务;shutdown() 唤醒 serve_forever 后优雅退出。""" global _pac_server if _pac_server: srv = _pac_server["server"] _pac_server = None - try: - # 先关闭 socket,让 serve_forever() 从 select/accept 中立即退出 - srv.socket.close() - except Exception: - pass - # 在后台线程里尝试优雅 shutdown,最多等 1 秒,避免阻塞 GUI + # 不要直接 close socket:serve_forever 的 select 仍注册着它,Windows 上 + # 对已关闭 socket 做 select 会抛 WinError 10038(非套接字操作)。 + # shutdown() 让 select 在 poll_interval(0.5s)内返回并正常退出。 + srv.daemon_threads = True # 请求处理线程不阻塞 serve_forever 退出 def _shutdown(): try: srv.shutdown() @@ -390,7 +388,20 @@ def _tls_start(remote_host, remote_port=443, use_tls=True, bypass_lan=False, sma svr = socket.socket(socket.AF_INET, socket.SOCK_STREAM) svr.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - svr.bind(("127.0.0.1", _TLS_LOCAL_PORT)) + # Windows 上 Hyper-V/WSL 的端口保留区间会动态变动,bind 可能瞬时被拒 + # (WinError 10013/10048);重试 3 次,间隔 1 秒 + last_err = None + for attempt in range(3): + try: + svr.bind(("127.0.0.1", _TLS_LOCAL_PORT)) + break + except OSError as e: + last_err = e + if attempt < 2: + log(f"端口 {_TLS_LOCAL_PORT} 绑定失败: {e},1 秒后重试 ({attempt + 1}/3)") + time.sleep(1) + else: + raise last_err svr.listen(32) svr.settimeout(1) _tls_forwarder = {"sock": svr, "running": True} @@ -1183,13 +1194,16 @@ class MainWindow(QWidget): self._proxy_set_by_us = True # 标记系统代理由本程序设置,退出时据此清理 log("验证代理连通性...") import urllib.request + # 显式 ProxyHandler 强制经本地转发器;默认 opener 会套用系统/环境代理 + # 设置(PAC 模式下注册表 ProxyEnable=0,urllib 认为无代理而绕开),造成假"可达" + opener = urllib.request.build_opener( + urllib.request.ProxyHandler({"http": f"{PROXY_HOST}:{PROXY_PORT}", + "https": f"{PROXY_HOST}:{PROXY_PORT}"})) # baidu 验证国内直连路径,google 验证境外隧道路径 test_urls = [("baidu.com", "http://www.baidu.com"), ("google.com", "http://www.google.com")] for name, url in test_urls: try: - req = urllib.request.Request(url) - req.set_proxy(f"{PROXY_HOST}:{PROXY_PORT}", "http") - urllib.request.urlopen(req, timeout=10) + opener.open(url, timeout=10) log(f" {name} 可达") except Exception as e: log(f" {name} 不可达: {e}")