#!/usr/bin/env python3 # -*- coding: utf-8 -*- import json import re import subprocess import urllib.parse import platform import shutil from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer HOST = "127.0.0.1" PORT = 39393 PING_TIMEOUT_MS = 1000 PING_COUNT = 1 def json_bytes(data): return json.dumps(data, ensure_ascii=False).encode("utf-8") def parse_ping_output(output: str): lower_output = output.lower() success = False if "ttl=" in lower_output: success = True if "reply from" in lower_output: success = True if "来自" in output and "ttl=" in lower_output: success = True times = [] matches = re.findall(r"(?:time|时间)\s*[=<]?\s*(\d+)\s*ms", output, re.IGNORECASE) for m in matches: try: times.append(int(m)) except Exception: pass response_time = round(sum(times) / len(times)) if times else None if success: status_text = "在线" else: if "请求超时" in output or "request timed out" in lower_output: status_text = "请求超时" elif "无法访问目标主机" in output or "destination host unreachable" in lower_output: status_text = "目标不可达" elif "一般故障" in output or "general failure" in lower_output: status_text = "网络故障" else: status_text = "离线" return success, response_time, status_text def run_ping(ip: str): cmd = ["ping", ip, "-n", str(PING_COUNT), "-w", str(PING_TIMEOUT_MS)] try: result = subprocess.run( cmd, capture_output=True, text=True, encoding="gbk", errors="ignore", timeout=8 ) except subprocess.TimeoutExpired: return {"ok": True, "online": False, "ip": ip, "responseTime": None, "statusText": "检测超时", "raw": "ping timeout"} except Exception as e: return {"ok": False, "online": False, "ip": ip, "responseTime": None, "statusText": "本地检测异常", "error": str(e)} output = ((result.stdout or "") + "\n" + (result.stderr or "")).strip() success, response_time, status_text = parse_ping_output(output) return {"ok": True, "online": success, "ip": ip, "responseTime": response_time, "statusText": status_text, "raw": output} class Handler(BaseHTTPRequestHandler): def _cors(self): self.send_header("Access-Control-Allow-Origin", "*") self.send_header("Access-Control-Allow-Methods", "GET, OPTIONS") self.send_header("Access-Control-Allow-Headers", "Content-Type") def do_OPTIONS(self): self.send_response(204) self._cors() self.end_headers() def do_GET(self): parsed = urllib.parse.urlparse(self.path) if parsed.path == "/health": body = json_bytes({ "ok": True, "service": "local-ping-proxy", "pythonVersion": platform.python_version(), "pingCount": PING_COUNT, "timeoutMs": PING_TIMEOUT_MS, "pingCommand": shutil.which('ping') is not None, }) self.send_response(200) self._cors() self.send_header("Content-Type", "application/json; charset=utf-8") self.send_header("Content-Length", str(len(body))) self.end_headers() self.wfile.write(body) return if parsed.path == "/ping": qs = urllib.parse.parse_qs(parsed.query) ip = (qs.get("ip", [""])[0] or "").strip() if not ip: body = json_bytes({"ok": False, "online": False, "statusText": "缺少 IP 参数", "error": "missing ip"}) self.send_response(400) self._cors() self.send_header("Content-Type", "application/json; charset=utf-8") self.send_header("Content-Length", str(len(body))) self.end_headers() self.wfile.write(body) return data = run_ping(ip) body = json_bytes(data) self.send_response(200) self._cors() self.send_header("Content-Type", "application/json; charset=utf-8") self.send_header("Content-Length", str(len(body))) self.end_headers() self.wfile.write(body) return body = json_bytes({"ok": False, "error": "not found"}) self.send_response(404) self._cors() self.send_header("Content-Type", "application/json; charset=utf-8") self.send_header("Content-Length", str(len(body))) self.end_headers() self.wfile.write(body) def log_message(self, format, *args): pass if __name__ == "__main__": server = ThreadingHTTPServer((HOST, PORT), Handler) print("=" * 60) print("本地检测助手已启动") print(f"地址: http://{HOST}:{PORT}") print("作用: 接收网页请求,在当前电脑本机执行 ping,并把结果返回给网页") print("说明: 只有这个窗口保持开启,网页里的“开始测试”才会生效") print("提醒: 测试期间请不要关闭这个窗口") print("=" * 60) server.serve_forever()