# -*- coding: utf-8 -*- r'''修 bench_lan_vs_relay.py:两条路用了同一个配对码。 == 问题 == 这个基准为了排除互相干扰,给"局域网"和"中转"两条路**各起一台全新被控端** (这个设计本身是对的)。但两台都用了写死的 `code = "246813"`。 中转有「一个配对码只能属于一台设备」的规则,于是: 第 1 台(跑局域网)用 246813 注册成功 -> stop_agent 杀掉进程,但**中转还以为它在线**(last_seen 没过期) 第 2 台(跑中转)也用 246813 注册 -> **被拒**(码被占),中转日志:配对码 246813 已被 ... 占用,拒绝 ... 而 start_agent 只检查"有没有设备在线" —— 第 1 台还挂着,于是**以为成功了** -> 接下来 attach 成功(码还绑在第 1 台那个 deviceId 上), 但**没有任何进程在回包** -> "连接阶段失败: timed out" 实测对照(_build/verify_code_reuse.py): 场景 A 同一个码 -> **不通** 场景 B 各用不同码 -> 通 **所以产品是好的,是这个基准脚本在"公正对比"这个目标上自己绊了自己。** == 顺带修掉两处 == 1. 那段「当前版本经中转只稳定支撑前 1~2 次交互」的注释是**过时的** —— 那个 bug 已经修好了(根因是一条 HTTP/1.1 连接被长轮询和发命令 两个线程同时用,撞状态机抛 "Idle" 把 client-relay 线程打死)。 注释要更新,否则会误导后人以为产品还有这个问题。 2. start_agent 只判断"有没有设备在线"太松 —— 会被上一台僵尸设备骗过。 加一个更硬的判据:看 deviceId 是不是**这一台**新生成的。 ''' from __future__ import annotations import sys from pathlib import Path ROOT = Path(r"C:\Users\shu\Desktop\deepseekagent\LanRemote") SRC = ROOT / "_tests" / "bench_lan_vs_relay.py" def patch(text, old, new, label): if old not in text: print(" ✗ 没找到:%s" % label) return text, False if text.count(old) != 1: print(" ! 出现 %d 次:%s" % (text.count(old), label)) return text, False print(" ✓ %s" % label) return text.replace(old, new, 1), True def main() -> int: text = SRC.read_text(encoding="utf-8") ok_all = True # ------------------------------------------------ 1. 两个码 text, ok = patch( text, ' code = "246813"', ''' # ⚠ 两条路必须用**不同的**配对码。 # 中转有「一个码只属于一台设备」的规则,而这个基准给每条路 # 各起一台全新被控端(为了排除互相干扰,设计是对的)。 # 要是两台共用一个码,第 2 台的注册会被拒,而第 1 台虽然 # 进程被杀了、中转还以为它在线 —— 于是"看着登记成功、 # 实际没人回包",表现为连接阶段超时。 # 实测对照见 _build/verify_code_reuse.py:同码不通、不同码通。 code = "246813" # 局域网那条路 relay_code = "975310" # 中转那条路,必须不同''', "两条路用不同的配对码") ok_all = ok_all and ok # ------------------------------------------------ 2. relay_connect 用 relay_code text, ok = patch( text, ''' reply = client.call({"cmd": "pair", "deviceId": "bench-relay", "deviceName": "基准测试手表", "code": code}, args.relay_timeout)''', ''' reply = client.call({"cmd": "pair", "deviceId": "bench-relay", "deviceName": "基准测试手表", "code": relay_code}, args.relay_timeout)''', "relay_connect 用 relay_code") ok_all = ok_all and ok # ------------------------------------------------ 3. 第二个 start_agent 传码 text, ok = patch( text, """ agent_proc, _data = start_agent() # attach 只是"接入中转",协议层还得 pair 一次才算鉴权完成 ——""", """ agent_proc, _data = start_agent(relay_code) # attach 只是"接入中转",协议层还得 pair 一次才算鉴权完成 ——""", "第二台用 relay_code") ok_all = ok_all and ok # ------------------------------------------------ 4. start_agent 接受码 + 硬判据 text, ok = patch( text, """ def start_agent(): data = Path(tempfile.mkdtemp(prefix="bench_agent_", dir=str(work))) store = Store(data) store.set_pairing_code(code)""", """ def start_agent(agent_code=None): agent_code = agent_code or code data = Path(tempfile.mkdtemp(prefix="bench_agent_", dir=str(work))) store = Store(data) store.set_pairing_code(agent_code)""", "start_agent 接受码参数") ok_all = ok_all and ok # 只判断"有没有在线设备"太松:上一台僵尸还挂着就会骗过它。 # 改成看有没有**在线且没被别的码占用**的设备,并且记录 deviceId 做比对。 text, ok = patch( text, """ if any(d.get("online") for d in snap.get("devices", [])): return proc, data raise RuntimeError("被控端 45 秒内没登记到中转")""", """ # ⚠ 不能只看"有没有设备在线" —— # 上一台被控端虽然进程被杀,中转还没超时清理它, # 于是这里会被僵尸设备骗过,以为新的一台登记成功了。 # 要求:存在一台在线、且配对码正好是**这一台要用的码** # 的设备。 live = [d for d in snap.get("devices", []) if d.get("online") and d.get("code") == agent_code] if live: return proc, data raise RuntimeError( "被控端 45 秒内没登记到中转(码 %s)" % agent_code)""", "start_agent 的判据改硬") ok_all = ok_all and ok # ------------------------------------------------ 5. 更新过时注释 text, ok = patch( text, """ # ⚠ 这里的轮数**故意比局域网少**,而且超时给得短。 # 原因:当前版本经中转只稳定支撑前 1~2 次交互,之后被控端 # 就不再回包(详见 _build/diag_relay_leg.py 的时间线: # attach/pair/ping#1 全 0ms 返回,ping#2 开始 relay 一直回 0 字节)。 # 这是中转链路的**产品 bug**,不是本测试的毛病 —— # 仓库现成的 _tests/test_relay_local.py 也是同样形状。 # 所以这里只做**有界**测量:拿得到就打点,拿不到就如实报错, # 绝不让整个基准卡死。""", """ # 【历史】这里原来写着"经中转只稳定支撑前 1~2 次交互", # 并据此把轮数调小、超时调短。那个产品 bug **已经修好了**: # # 根因是一条 HTTP/1.1 连接被两个线程同时用 —— # relay-link 线程的 /v1/agent/poll 要挂 180 秒(长轮询), # client-relay 线程同时 POST /v1/agent/send 发命令, # HTTP/1.1 一条连接同时只能一个在途请求,撞上状态机就抛 # CannotSendRequest("Idle"),异常冒到 server.py 的 # _client_loop 外面把线程打死 —— 之后电脑再也不回包。 # # 修法:发命令走独立连接 + 锁盖住整个请求周期 + # _client_loop 兜住回包异常。_tests/test_relay_local.py # 从"完全卡死"变成 21/21 全过。 # # 所以现在两条路用同样的轮数和超时,口径才一致。 # (用户可通过 --relay-rounds / --relay-timeout 自行调整。)""", "更新过时注释") ok_all = ok_all and ok if not ok_all: print() print(" 有锚点没匹配,没有写回") return 1 SRC.write_text(text, encoding="utf-8", newline="\n") print() print(" 已写回 bench_lan_vs_relay.py") return 0 if __name__ == "__main__": sys.exit(main())