import time import logging from datetime import datetime, timezone from models import Proxy, db from extensions import db as _db_ext log = logging.getLogger("socks.health") TEST_URL = "http://httpbin.org/get" def check_proxy(proxy): """检查单个 SOCKS5 代理是否可用。返回 {status, latency_ms, error}""" import socks import socket host, port = proxy.host, proxy.port timeout = 10 status = "unknown" latency = None error = None orig_getaddrinfo = socket.getaddrinfo orig_socket = socket.socket try: # 使用 PySocks 创建 SOCKS5 隧道 s = socks.socksocket() s.settimeout(timeout) s.set_proxy(socks.SOCKS5, host, port, username=proxy.username or None, password=proxy.password or None) t0 = time.time() # 尝试连接到测试 URL(httpbin.org) s.connect(("httpbin.org", 80)) elapsed = time.time() - t0 latency = round(elapsed * 1000, 1) # 发送一个简单 HTTP HEAD 请求验证隧道通畅 s.sendall(b"HEAD /get HTTP/1.1\r\nHost: httpbin.org\r\nConnection: close\r\n\r\n") resp = s.recv(1024) s.close() if resp and (b"200" in resp[:4] or b"301" in resp[:4] or b"302" in resp[:4]): status = "online" else: status = "online" # 连接成功即算在线(某些代理只允许特定目标) except Exception as e: status = "offline" error = str(e)[:200] log.warning("Proxy %s:%d check failed: %s", host, port, e) finally: socket.getaddrinfo = orig_getaddrinfo socket.socket = orig_socket return {"status": status, "latency_ms": latency, "error": error} def run_health_checks(): """对所有 enabled 的代理运行健康检测,更新数据库,返回结果摘要。""" proxies = Proxy.query.filter_by(enabled=True).all() results = [] online_count = 0 for p in proxies: r = check_proxy(p) p.status = r["status"] p.latency_ms = r["latency_ms"] p.last_check = datetime.now(timezone.utc) if r["status"] == "online": online_count += 1 results.append({ "id": p.id, "name": p.name, "host": p.host, "port": p.port, **r, }) _db_ext.session.commit() return { "checked": len(results), "online": online_count, "offline": len(results) - online_count, "details": results, }