# -*- coding: utf-8 -*- r'''复刻 bench_lan_vs_relay 的中转那一半,抓被控端日志。 我自己那个 ins_relay_flow.py 是中转 4/4 通过的, 但 bench_lan_vs_relay.py 的中转那半报"连接阶段失败: timed out"。 差异一定在设置里,所以要逐项对齐 bench 的写法: * RELAY_POLL=2(我那边是 5) * 用 YKXMZ_DATA 指到临时目录 + --silent(我那边直接写 windows/data) * 每条路起**一台全新的**被控端 * 先 attach 再 pair(bench 的 relay_connect 就是这么做的) 把每一步的耗时和被控端日志都打出来。 ''' from __future__ import annotations import json import os import socket import subprocess import sys import tempfile import time import urllib.error import urllib.request from pathlib import Path ROOT = Path(__file__).resolve().parent.parent WINDOWS = ROOT / "windows" UA = {"User-Agent": "LanRemote/1.8.2"} CODE = "975310" def free_port() -> int: s = socket.socket() s.bind(("127.0.0.1", 0)) p = s.getsockname()[1] s.close() return p def post(base, path, body=b"", headers=None, timeout=20.0): h = dict(UA) h.update(headers or {}) req = urllib.request.Request(base + path, data=body, headers=h, method="POST") try: with urllib.request.urlopen(req, timeout=timeout) as resp: return resp.status, resp.read() except urllib.error.HTTPError as exc: return exc.code, exc.read() except Exception as exc: # noqa: BLE001 return 0, str(exc).encode() def get(base, path, timeout=5.0): req = urllib.request.Request(base + path, headers=UA) try: with urllib.request.urlopen(req, timeout=timeout) as resp: return resp.status, resp.read() except Exception as exc: # noqa: BLE001 return 0, str(exc).encode() def main() -> int: work = Path(tempfile.mkdtemp(prefix="benchdiag_")) relay_port = free_port() agent_port = free_port() base = "http://127.0.0.1:%d" % relay_port relay_proc = agent_proc = None relay_log = agent_log = None try: print(" 中转端口 %d 被控端端口 %d" % (relay_port, agent_port)) # ---- 1. 本地中转(对齐 bench:RELAY_POLL=2)---- rdata = work / "r" rdata.mkdir(parents=True, exist_ok=True) env = dict(os.environ) env.update({"RELAY_PORT": str(relay_port), "RELAY_BIND": "127.0.0.1", "RELAY_DATA": str(rdata), "RELAY_POLL": "2", "PYTHONIOENCODING": "utf-8", "PYTHONUNBUFFERED": "1"}) relay_log = open(work / "relay.log", "wb") relay_proc = subprocess.Popen( [sys.executable, "-u", str(ROOT / "relay" / "relay.py")], env=env, stdout=relay_log, stderr=subprocess.STDOUT) for _ in range(60): time.sleep(0.5) if get(base, "/v1/health")[0] == 200: break print(" 中转就绪") # ---- 2. 被控端(对齐 bench:YKXMZ_DATA + --silent + 独立锁端口)---- sys.path.insert(0, str(WINDOWS)) from lanremote.config import Store adata = work / "a" adata.mkdir(parents=True, exist_ok=True) store = Store(adata) store.set_pairing_code(CODE) store.set("setup_done", True) store.set("allow_new_pairing", True) store.set("close_pair_after_first", False) store.set("server_name", "基准测试电脑") store.set("relay_url", base) store.set("port", agent_port) aenv = dict(os.environ) aenv.update({"YKXMZ_DATA": str(adata), "YKXMZ_LOCK_PORT": str(free_port()), "PYTHONIOENCODING": "utf-8", "PYTHONUNBUFFERED": "1"}) agent_log = open(work / "agent.log", "wb") agent_proc = subprocess.Popen( [sys.executable, "-u", str(WINDOWS / "run_agent.py"), "--silent", "--port", str(agent_port)], cwd=str(WINDOWS), env=aenv, stdout=agent_log, stderr=subprocess.STDOUT) online = False for _ in range(45): time.sleep(1.0) if agent_proc.poll() is not None: print(" ✗ 被控端启动即退出,返回码 %s" % agent_proc.returncode) break st, body = get(base, "/v1/health", timeout=4) if st == 200: snap = json.loads(body.decode()) if any(d.get("online") for d in snap.get("devices", [])): online = True break print(" 被控端登记: %s" % ("成功" if online else "**失败**")) if not online: return 1 # ---- 3. 完全照 bench 的 relay_connect 做 ---- print() print(" ── 照 bench 的顺序:attach -> pair ──") t0 = time.time() st, body = post(base, "/v1/client/attach", json.dumps( {"code": CODE, "name": "基准测试手表"}).encode(), {"Content-Type": "application/json"}) att = json.loads(body.decode()) session = att.get("session", "") print(" attach: %dms ok=%s session=%s" % (int((time.time() - t0) * 1000), att.get("ok"), session[:8] if session else "无")) payload = (json.dumps({"cmd": "pair", "deviceId": "bench-relay", "deviceName": "基准测试手表", "code": CODE}, ensure_ascii=False) + "\n").encode() t0 = time.time() st, _ = post(base, "/v1/client/send", payload, {"X-Session": session}) print(" send pair: %dms status=%s" % (int((time.time() - t0) * 1000), st)) # 轮询等回复,每轮都打点 buf = b"" deadline = time.time() + 40 got = None round_no = 0 while time.time() < deadline: round_no += 1 remaining = deadline - time.time() t0 = time.time() st, chunk = post(base, "/v1/client/poll", b"", {"X-Session": session}, min(10.0, remaining)) buf += chunk print(" poll#%d: %dms status=%s 收到 %d 字节" % (round_no, int((time.time() - t0) * 1000), st, len(chunk))) while b"\n" in buf: line, _, buf = buf.partition(b"\n") if not line.strip(): continue msg = json.loads(line.decode()) if "event" in msg: continue got = msg break if got is not None: break if got is None: print(" ✗ 40 秒内没等到 pair 的回复") else: print(" ✓ pair 回复: %s" % json.dumps(got, ensure_ascii=False)[:120]) # ---- 4. 再来几次 ping ---- if got is not None: for i in range(5): payload = (json.dumps({"cmd": "ping"}) + "\n").encode() post(base, "/v1/client/send", payload, {"X-Session": session}) deadline = time.time() + 15 ok = False while time.time() < deadline: st, chunk = post(base, "/v1/client/poll", b"", {"X-Session": session}, 8.0) buf += chunk while b"\n" in buf: line, _, buf = buf.partition(b"\n") if not line.strip(): continue msg = json.loads(line.decode()) if "event" in msg: continue ok = True break if ok: break print(" ping#%d: %s" % (i + 1, "OK" if ok else "**超时**")) # ---- 5. 被控端日志 ---- print() print(" ════ 被控端日志(最后 25 行)════") agent_log.flush() for line in (work / "agent.log").read_text( encoding="utf-8", errors="replace").splitlines()[-25:]: print(" " + line[:120]) print() print(" ════ 进程还活着吗 ════") print(" agent poll = %s" % agent_proc.poll()) print() print(" ════ 中转日志(最后 10 行)════") relay_log.flush() for line in (work / "relay.log").read_text( encoding="utf-8", errors="replace").splitlines()[-10:]: print(" " + line[:120]) return 0 finally: for p in (agent_proc, relay_proc): if p is not None: try: p.terminate() p.wait(timeout=6) except Exception: # noqa: BLE001 try: p.kill() except Exception: # noqa: BLE001 pass import shutil shutil.rmtree(work, ignore_errors=True) if __name__ == "__main__": sys.exit(main())