test(net/udp): 阶段 16 台架加固 + 真机首跑证据回填(#73)
第一次上真机把台架的两处软肋照出来了,都不是被测代码的问题: - peer 死在 Windows 的 ICMP 10054:板子还没 bind 时那一下端口不可达会被记在套接句上, 下一次 recvfrom 才抛出来。本机 CPython 3.14.7 既没有 socket.SIO_UDP_CONNRESET 也不接受 它的数值形式,所以主力防线改成把 10054 当「这一轮没收到」继续重试,并在回路自检里加 冷端口探针——探针走脚本真正在用的 _recv_maybe,另拿裸套接句证明这台机器确实会回 ICMP, 否则那条判定就没有牙。 - 单步预算错位:板端每步等 25 s,peer 每步只等 8 s,一步超时后整条梯子全灭。现在 peer 每步都重发、单步预算 --step-budget 默认 35 s(小于 26 s 打警告);板端 udp_bench_expect 按载荷第 0 字节的步号自愈,比本步小的旧包丢掉继续等(上限 UDP_BENCH_STALE_MAX = 8), 比本步大的直接报出来。DBG_WARN 在本工程不存在、字符串字面量必须纯 ASCII,两处已改。 真机已拿到的证据:TC-NET-1601a/b/c(建 socket、bind :6001、net_udp_get_mode 回 SERVER)与 TC-NET-1602(服务端形态收到 20 B、addrlen/sin_family/源端口全对、逐字节回显、形态翻成 CLIENT)全绿——剥 8 字节信息头与源地址回填在真芯片上成立;②③④⑤ 因板子当晚两次失电 (19:19、21:0x,SWD No target connected + ping 不通)未测,指南 §12.6 与覆盖率口径第 4 轮 的状态条据此改写。产品态(宏回默认)重新构建 0 Error 0 Warning,Code 249,124 / RO 29,836 / RW 2,052 / ZI 149,996 与本阶段之前逐字节一致,阶段 16 在产品里被预处理干净。
This commit is contained in:
@@ -54,9 +54,61 @@ def echo_one(sock, deadline, stop):
|
||||
sock.sendto(echo[off:off + CHIP_SEND_BUF], addr)
|
||||
|
||||
|
||||
def check_cold_port() -> bool:
|
||||
"""对端脚本必须先扛住「板子还没把 socket 打开」这段时间——真机第一次上台架就死在这。
|
||||
|
||||
往一个确定没人监听的端口发一颗包,Windows 会收下 ICMP 端口不可达并记在套接句上,
|
||||
下一次 recvfrom 就抛 WinError 10054。这里走**脚本真正在用的那条路**(Peer._recv_maybe),
|
||||
要求它返回 None(= 这一轮没收到,继续重试),而不是把异常抛穿。裸套接句那一段只是
|
||||
证明这台机器确实会回 ICMP——不然这个探针没有牙,别把它当唯一证据。
|
||||
"""
|
||||
dead = 6099 # 假板子只绑 6001/6002,这个端口确定空着
|
||||
ok = False
|
||||
p = peer.Peer("127.0.0.1", 1.0)
|
||||
s = peer.new_peer_socket()
|
||||
try:
|
||||
s.settimeout(1.0)
|
||||
s.sendto(b"\x00", ("127.0.0.1", dead))
|
||||
try:
|
||||
r = p._recv_maybe(s, 1.0)
|
||||
if r is None:
|
||||
ok = True
|
||||
else:
|
||||
print("[selftest] 冷端口探针:竟然收到了数据报,探针失效", flush=True)
|
||||
except ConnectionResetError:
|
||||
print("[selftest] 冷端口探针:Peer._recv_maybe 把 WinError 10054 抛穿了"
|
||||
"(真机上对端会当场死在第一条用例)", flush=True)
|
||||
finally:
|
||||
s.close()
|
||||
except OSError as exc:
|
||||
print("[selftest] 冷端口探针建句/发送失败:{0}".format(exc), flush=True)
|
||||
|
||||
raw = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
try:
|
||||
raw.settimeout(1.0)
|
||||
raw.sendto(b"\x00", ("127.0.0.1", dead))
|
||||
try:
|
||||
raw.recvfrom(64)
|
||||
print("[selftest] 裸套接句也收到了数据报(探针无牙,仅记录)", flush=True)
|
||||
except ConnectionResetError:
|
||||
print("[selftest] 裸套接句抛 WinError 10054 —— 这台机器确实会踩,"
|
||||
"上面那条判定有牙", flush=True)
|
||||
except socket.timeout:
|
||||
print("[selftest] 裸套接句超时(本机没回 ICMP,探针无牙,仅记录)", flush=True)
|
||||
except OSError as exc:
|
||||
print("[selftest] 裸套接句探针失败:{0}".format(exc), flush=True)
|
||||
finally:
|
||||
raw.close()
|
||||
return ok
|
||||
|
||||
|
||||
def main() -> int:
|
||||
if hasattr(sys.stdout, "reconfigure"):
|
||||
sys.stdout.reconfigure(encoding="utf-8", errors="replace")
|
||||
if not check_cold_port():
|
||||
print("=== UDP Peer Loopback Self-Test: FAIL(冷端口探针没过,先修 new_peer_socket)===",
|
||||
flush=True)
|
||||
return 1
|
||||
deadline = time.time() + RUNOUT_S
|
||||
stop = threading.Event()
|
||||
for listen_port in LISTEN_PORTS:
|
||||
@@ -71,8 +123,10 @@ def main() -> int:
|
||||
time.sleep(0.3)
|
||||
|
||||
# --late 必须落在板端 7000~25000 ms 判定窗内;这里取 9 秒,比默认 12 秒省一点台架外时间
|
||||
# --step-budget 8:假板子秒回,用不到真机那 35 秒预算;真机台架千万别照抄这个值
|
||||
#(真机上必须盖过板端单步 25 秒等待,否则一次超时就整条梯子错位)。
|
||||
sys.argv = ["udp_queue_bench.py", "--board", "127.0.0.1",
|
||||
"--first-wait", "15", "--timeout", "6",
|
||||
"--first-wait", "15", "--timeout", "6", "--step-budget", "8",
|
||||
"--probe-silence", "2.0", "--late", "9.0"]
|
||||
try:
|
||||
rc = peer.main()
|
||||
|
||||
Reference in New Issue
Block a user