Files
STM32F4-Base/MDK-ARM/tcp_reconnect_probe.py
T
wandering1 95beaab313 refactor(编号): 第一批清号——登记簿旧号/阶段号/台架阶段裸形三族清零,位号目录排除
按 §9 批次一执行。**这批的关键发现是「看着像 62 处编号,实际能算编号的只有一小半」**,所以先分类再动手,
没有按站点数硬清:

· **真·编号(已改名)**:`N-6`(`MDK-ARM/tcp_reconnect_probe.py` 的「TRAP-NET-17 / N-6
」→ 只留 TRAP 锚点
  + 中文说明);台架阶段裸形 `T0`~`T3` 全部转成场景名——`e11_keepalive_probe.py` 的「静默对端 / 正常空闲存活」、
  `ftp_auth_bench.py` 的「匿名登录 / 未登录直接 NLST / 错口令锁定」、`serial_tool.py` 自检里的「行拆分 /
  解码回退链 / 时间戳行格式 / 端口建议」、`net_test_task.c` 与 `CH395F_Test_Guide.md` 的「空闲存活场景」、
  网络自愈设计里的「三路审计」。另把 `test/host/iec104_master.py` 的变量 `T0` 改成 `t_start`(那是起始时刻
  变量,被裸形族误抓)。
· **不是编号、但被误判(按 §4.4 处理,不改语料)**:`tools/netlist_semantics/` 里的 `T1` 是**网络变压
器位号**
  (硬件知识库),整目录进 `excluded_paths`,与 `docs/硬件平台/` 同口径;`BSD_Socket_API_函数调用关系.
md` 的
  `T1/T2/T3` 是 **Mermaid 节点名**,已改成语义节点(`RetryTick`/`PendTick`/`PendShimTic
k`/`ReconcileTick`)——
  图本身没变,只是节点不再叫字母号;`docs/门禁说明/gitlint.md` 里的 `T2` 是 **gitlint 自己的规则名**(不是本仓
  的号),已改写成规则描述;`docs/算法/傅氏算法详解.md` 的 `N-1` 是**数学记号**(求和上界 `x(N-1)`),保留不动,
  并在 §4.4 登记为「唯一已知误判」。
· **规范自身两处示例**顺手去掉字面量:`S3 / TM-PHY-01 / DP-03` → `S3 / TM-… / DP-…`;「报告 N-
16」→「报告 +
  登记簿旧号」。

结果(用门禁接口复测):`TM` 族 0 引用、`T-TEST` 族只剩规范自身 2 处示例、`N` 族只剩数学记号 1 处;
`F-*`(38 站点 / 12 个号,代码注释里的网络设计条目号)**留下批单独做**,因为每处要读懂语义、不能替换字面量。

口径变更已写进 §4.4:位号目录 `tools/netlist_semantics/` 进排除面;`N-1` 声明为已知误判。歧义面收窄后,
新错误级仍为 0、`id-check --strict` exit 0。

⚠ 这批**刻意没有复活「别名表洗白」那条路**:`N-6` 这类死号不进 `id_aliases.json`,让门禁继续咬将来的残留
引用(与 FTP 死号同一处理,见 §5.7)。
2026-10-27 07:39:00 +08:00

82 lines
3.2 KiB
Python

# -*- coding: utf-8 -*-
# TRAP-NET-17 复现探针:快速重连到设备监听器: rapid reconnect to a device listener.
#
# Failure under test (original report): after a rapid reconnect (<~1s) on a
# relistened socket, the chip completes the handshake but the MCU never gets
# standalone accept -> the socket stays LISTENING -> later the chip auto-closes it
# -> THE LISTENER DIES PERMANENTLY (all later SYNs refused, no self-recovery).
#
# Judgement is purely client-side (no firmware probes):
# * every round must (re)connect successfully within --timeout
# * on failure the probe keeps retrying for --recover-s to distinguish
# "transient" from "permanently dead listener"
#
# Usage: tcp_reconnect_probe.py [--host 192.168.1.100] [--port 21] [--rounds 50]
# [--gap-ms 600] [--timeout 3] [--recover-s 10]
# [--banner] (read one line after connect, e.g. FTP 220)
# Output:
# TS <epoch>
# ROUND <n> gap=<ms> connect=<OK|FAIL:reason> recovered=<na|yes|no>
# SUMMARY rounds=<n> ok=<n> fail=<n> unrecovered=<n>
# Exit: 0 all rounds connected / 1 any failure / 2 usage
import argparse, socket, sys, time
def log(m):
sys.stdout.write(m + "\n"); sys.stdout.flush()
def try_connect(host, port, timeout, banner):
s = socket.create_connection((host, port), timeout=timeout)
if banner:
s.settimeout(timeout)
try:
s.recv(256) # e.g. FTP "220 ..." greeting
except socket.timeout:
pass
return s
def main():
ap = argparse.ArgumentParser()
ap.add_argument("--host", default="192.168.1.100")
ap.add_argument("--port", type=int, default=21)
ap.add_argument("--rounds", type=int, default=50)
ap.add_argument("--gap-ms", type=int, default=600)
ap.add_argument("--timeout", type=float, default=3.0)
ap.add_argument("--recover-s", type=float, default=10.0)
ap.add_argument("--banner", action="store_true")
a = ap.parse_args()
nok = nfail = nunrec = 0
for n in range(1, a.rounds + 1):
log("TS %d" % int(time.time()))
res = "OK"
recovered = "na"
try:
s = try_connect(a.host, a.port, a.timeout, a.banner)
s.close()
except Exception as e:
res = "FAIL:%s" % type(e).__name__
# keep probing: transient (relisten window) or permanently dead?
t0 = time.time()
recovered = "no"
while time.time() - t0 < a.recover_s:
time.sleep(0.5)
try:
s = try_connect(a.host, a.port, a.timeout, a.banner)
s.close()
recovered = "yes"
break
except Exception:
pass
ok = (res == "OK")
nok += 1 if ok else 0
nfail += 0 if ok else 1
if not ok and recovered != "yes":
nunrec += 1
log("ROUND %d gap=%d connect=%s recovered=%s" % (n, a.gap_ms, res, recovered))
if a.gap_ms > 0 and n < a.rounds:
time.sleep(a.gap_ms / 1000.0)
log("SUMMARY rounds=%d ok=%d fail=%d unrecovered=%d" % (a.rounds, nok, nfail, nunrec))
return 0 if nfail == 0 else 1
sys.exit(main())