#!/usr/bin/env python3 """头阻塞专项台架探针(timeSync 侧,A/B 对照用)。 场景(历史审计的头阻塞触发形态,属主文档已于 2026-10-15 删除): 1. 一个"挂起"的对时客户端连上 :8888 后不发任何数据(空闲/故障客户端, 常见工况)——旧固件在此触发 30s 阻塞 net_recv 独占 netTask, 全体消费者的队列消息停摆最长 30s; 2. 2s 后从 PC 发起 FTP(:21) 连接,测 banner 到达时延 = 队列响应性: 修复前 ≈ 25~30s(队头冻结),修复后 ≈ 数百 ms 内; 3. 34s 时 30s 总预算应已耗尽:设备拆掉该会话(net_drop_conn: FIN 关闭 + 本层收口 + 重监听),随后**本探针模拟守规矩对端**: close 自己的 socket; 4. 芯片回 CLOSED(TC-NET-204 模式 A:TIME_WAIT 过渡 ~150ms)→ relisten, :8888 应能再接受新连接(3s 重试窗内);新连接发完整 8 字节 TIME 包 → 设备应落 "wall clock updated"。 ⚠ 定案口径(TRAP-NET-07 定案更正 2026-10-11 + 《网络自愈架构设计》§5.2 E13):CH395F close **无 RST 语义**。若对端内核存活(app 挂起但 socket 不关,内核自动 ACK FIN), 芯片停在 FIN_WAIT_2、监听恢复受限于对端 close——探针第 3 步的 close 正是 模拟"对端配合"这一可恢复边界;不配合场景的兜底是芯片 TIMEOUT/keepalive 拆僵尸会话(E13 定案,30~90s)。 用法: python e02_probe.py 退出码: 0 = 队列响应性 PASS;1 = FAIL;2 = 探针自身异常 """ import socket import sys import time DEV = "192.168.1.100" tag = sys.argv[1] if len(sys.argv) > 1 else "run" t0 = time.time() def log(msg): print("[%s] %9.3f %s" % (tag, time.time() - t0, msg), flush=True) def main(): # 1. 挂起对时客户端:连接后不发任何数据 ts = socket.create_connection((DEV, 8888), timeout=5) log("time-sync conn open (hung client: sends nothing)") # 2. 2s 后测 FTP banner 时延(队列响应性) time.sleep(2.0) t1 = time.time() banner = None try: ftp = socket.create_connection((DEV, 21), timeout=35) banner = ftp.recv(256) dt = time.time() - t1 log("FTP banner after %8.1f ms: %r" % (dt * 1000, banner[:40])) ftp.close() except Exception as e: dt = time.time() - t1 log("FTP connect FAIL after %8.1f ms: %r" % (dt * 1000, e)) dt = None queue_ok = (banner is not None) and (dt is not None) and (dt < 3.0) log("queue responsiveness: %s (banner %s ms)" % ("PASS" if queue_ok else "FAIL", ("%.0f" % (dt * 1000)) if dt is not None else "n/a")) # 3. 等 30s 总预算耗尽(设备应拆会话:FIN + 本层收口 + 重监听) remain = 34.0 - (time.time() - t0) if remain > 0: time.sleep(remain) # 3b. 模拟守规矩对端:设备已发 FIN(drop_conn),本端 close 配合拆链。 # CH395F close 无 RST 语义(TRAP-NET-07 定案更正):不配合的内核存活对端 # 会把芯片停在 FIN_WAIT_2,监听恢复受限于对端 close——不在本探针判据内。 try: ts.close() log("hung client socket closed (cooperate with device FIN)") except OSError: pass # 设备已先拆连,本地 close 报 ECONNRESET 属预期 # 4. 监听应已重建(芯片 TIME_WAIT 过渡 ~150ms + relisten):3s 重试窗 t2 = time.time() listener_ok = False ts2 = None while time.time() - t2 < 3.0: try: ts2 = socket.create_connection((DEV, 8888), timeout=2) break except Exception as e: log("second time-sync conn retry: %r" % (e,)) time.sleep(0.3) if ts2 is not None: dt2 = time.time() - t2 epoch = int(time.time()) log("new time-sync conn in %6.0f ms, sent full TIME packet (epoch=%d)" % (dt2 * 1000, epoch)) ts2.sendall(b"TIME" + epoch.to_bytes(4, "big")) time.sleep(1.5) # 留时间给设备处理并打日志 ts2.close() listener_ok = True else: log("second time-sync conn FAIL (3s window)") log("OVERALL: queue=%s listener=%s" % ("PASS" if queue_ok else "FAIL", "PASS" if listener_ok else "FAIL")) return 0 if (queue_ok and listener_ok) else 1 if __name__ == "__main__": try: sys.exit(main()) except Exception as e: print("[%s] PROBE ERROR: %r" % (tag, e), flush=True) sys.exit(2)