# -*- coding: utf-8 -*- """验证「手表能不能判断出电脑和自己同网络」的数据链路。 用户要求: 手表连上之后,如果发现电脑和自己**在同一个网络**,就切局域网直连; 不在同一个网络就继续用中转。 要判断这个,手表需要知道电脑的局域网 IP 和端口。链路是: 被控端上报 lanIps/port -> 中转存在 Device 上 -> 快照和设备信息里带给手表 -> 手表拿自己的 IP 比网段 这个测试验证前三步(Java 那步靠编译 + 代码审查)。 """ from __future__ import annotations import json import os import shutil import socket import subprocess import sys import tempfile import time import urllib.error import urllib.request from pathlib import Path _UA = {"User-Agent": "LanRemote/1.4.0"} ROOT = Path(__file__).resolve().parent.parent sys.path.insert(0, str(ROOT / "windows")) RELAY_PORT = 8096 BASE = f"http://127.0.0.1:{RELAY_PORT}" passed = 0 failed = 0 def check(name, ok, detail=""): global passed, failed if ok: passed += 1 print(" [PASS] " + name) else: failed += 1 print(" [FAIL] " + name + " " + str(detail)) def post(path, payload=None, headers=None): body = json.dumps(payload or {}, ensure_ascii=False).encode("utf-8") hdrs = dict(_UA) hdrs["Content-Type"] = "application/json" hdrs.update(headers or {}) req = urllib.request.Request(BASE + path, data=body, method="POST", headers=hdrs) try: with urllib.request.urlopen(req, timeout=15) as resp: return resp.status, json.loads(resp.read().decode("utf-8")) except urllib.error.HTTPError as exc: raw = exc.read().decode("utf-8", "replace") try: return exc.code, json.loads(raw) except Exception: # noqa: BLE001 return exc.code, {"raw": raw[:150]} def get(path, headers=None): hdrs = dict(_UA) hdrs.update(headers or {}) req = urllib.request.Request(BASE + path, headers=hdrs) try: with urllib.request.urlopen(req, timeout=15) as resp: return resp.status, resp.read().decode("utf-8", "replace") except urllib.error.HTTPError as exc: return exc.code, exc.read().decode("utf-8", "replace") def my_lan_ip() -> str: """本机在局域网的地址(和被测控端用的是同一招)。""" try: probe = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) try: probe.connect(("8.8.8.8", 80)) return probe.getsockname()[0] finally: probe.close() except OSError: return "" def main() -> int: work = Path(tempfile.mkdtemp(prefix="lan_detect_")) relay = None try: relay_data = work / "relaydata" relay_data.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(relay_data), "RELAY_POLL": "6", "PYTHONIOENCODING": "utf-8", "PYTHONUNBUFFERED": "1", }) relay = subprocess.Popen( [sys.executable, "-u", str(ROOT / "relay" / "relay.py")], env=env, stdout=open(work / "relay.log", "wb"), stderr=subprocess.STDOUT) up = False for _ in range(40): time.sleep(0.5) try: status, _ = get("/v1/health") if status == 200: up = True break except Exception: # noqa: BLE001 continue check("本地中转起来了", up) if not up: print((work / "relay.log").read_text(encoding="utf-8", errors="replace")[-500:]) return 1 mine = my_lan_ip() print(f" (本机局域网地址:{mine or '拿不到'})") print() print("一、被控端上报局域网 IP 和端口") status, first = post("/v1/agent/register", { "deviceId": "pc-lan", "code": "445566", "name": "带局域网的电脑", "version": "1.4.0", "lanIps": ["192.168.2.100", "10.0.0.5"], "port": 8765}) check("登记成功", first.get("ok") is True, first) status, body = get("/v1/health") devices = json.loads(body).get("devices", []) target = next((d for d in devices if d.get("deviceId") == "pc-lan"), None) check("设备在列表里", target is not None, devices) if target: check("lanIps 存下来了", target.get("lanIps") == ["192.168.2.100", "10.0.0.5"], target.get("lanIps")) check("port 存下来了", target.get("port") == 8765, target.get("port")) print() print("二、手表接入时能拿到这两个值") status, attach = post("/v1/client/attach", {"code": "445566", "name": "手表"}) check("接入成功", attach.get("ok") is True, attach) check("接入响应里有 lanIps", attach.get("lanIps") == ["192.168.2.100", "10.0.0.5"], attach.get("lanIps")) check("接入响应里有 port", attach.get("port") == 8765, attach.get("port")) print() print("三、脏数据要挡住(不能把乱七八糟的东西转给手表)") post("/v1/agent/register", { "deviceId": "pc-dirty", "code": "447788", "name": "脏数据", "version": "1.4.0", "lanIps": ["192.168.1.1", "not-an-ip", "999.1.1.1", "