diff --git a/src/hostdemo.rs b/src/hostdemo.rs index 3cdbd82..0b87899 100644 --- a/src/hostdemo.rs +++ b/src/hostdemo.rs @@ -1877,40 +1877,78 @@ fn session_loop(sock: &UdpSocket, dst: &str, target: &str, rng: &mut Rng, b12: u } /// ★UDP 31772 通用中继 (udprelay 子命令): 跨网段主机验证用 -/// 客户端(任意网段) ↔ sockA(listen_addr) | sockB(src_ip:0) ↔ Butler +/// 客户端(任意网段) ↔ sockA(listen_addr) | 每客户端独立 sockB(src_ip:0) ↔ Butler /// 发往 Butler 的帧源 IP = src_ip (123.113, 绕过 AECH 固件源过滤); -/// Butler 应答(单播到 sockB) 转发回最后一个客户端; Butler 广播(到 sockA) 也转发。 +/// Butler 应答(单播到该客户端的 sockB) 原路转发回该客户端; Butler 广播(到 sockA) 广播给所有客户端。 +/// +/// 2026-09-13 重构: 旧版用单个 `client` 变量记录"最后发包者", 任何客户端在 +/// 请求与应答之间只要有另一个客户端发了包(如 SL 30fps 灌流 vs 低频扫描套接字), +/// 应答就会被投递给错误的套接字 —— SL 扫描因此永远收不到 DUB 上行。 +/// 现在每个客户端持有独立的上行 socket, Butler 按目的端口(=该 socket)天然 +/// 区分客户端, 应答无歧义回原客户端; 上限 8 客户端, 淘汰最旧。 pub fn udp_relay(listen_addr: &str, butler_ip: &str, src_ip: &str) { let a = open_reuse(listen_addr); - let b = open_reuse(&format!("{}:0", src_ip)); - a.set_read_timeout(Some(Duration::from_millis(2))).unwrap(); - b.set_read_timeout(Some(Duration::from_millis(2))).unwrap(); + a.set_read_timeout(Some(Duration::from_millis(1))).unwrap(); let bdst = format!("{}:31772", butler_ip); - let mut client: Option = None; + let butler_sa: std::net::SocketAddr = bdst.parse().unwrap(); + struct Up { + addr: std::net::SocketAddr, + sock: UdpSocket, + } + let mut ups: Vec = Vec::new(); let mut ba = [0u8; 4096]; let mut bb = [0u8; 4096]; let (mut n_fwd, mut n_rev) = (0u64, 0u64); - println!("[RELAY] {} <-> Butler {} (源伪装 {}), Ctrl+C 停止", listen_addr, bdst, src_ip); + println!("[RELAY] {} <-> Butler {} (源伪装 {}, 每客户端独立上行), Ctrl+C 停止", listen_addr, bdst, src_ip); loop { - if let Ok((n, from)) = a.recv_from(&mut ba) { - if from.ip().to_string() == butler_ip { - // Butler 广播/事件 → 客户端 - if let Some(c) = client { let _ = a.send_to(&ba[..n], c); n_rev += 1; } - } else { - client = Some(from); - let _ = b.send_to(&ba[..n], &bdst); - n_fwd += 1; - if n_fwd == 1 { println!("[RELAY] 客户端 = {}", from); } + // Butler → 各客户端: 逐上行 socket 收(非阻塞, 无歧义归属) + for up in ups.iter_mut() { + loop { + match up.sock.recv_from(&mut bb) { + Ok((n, from)) => { + if from.ip().to_string() == butler_ip { + let _ = a.send_to(&bb[..n], up.addr); + n_rev += 1; + } + } + Err(ref e) if e.kind() == std::io::ErrorKind::WouldBlock => break, + Err(_) => break, + } } } - if let Ok((n, _from)) = b.recv_from(&mut bb) { - if let Some(c) = client { - let _ = a.send_to(&bb[..n], c); - n_rev += 1; + // 客户端(或 Butler 侧广播) → listen socket + if let Ok((n, from)) = a.recv_from(&mut ba) { + // 2026-09-13: Butler 判定改为完整 socket (IP:31772)。旧逻辑只比 + // 对 IP —— DUT 侧内部发现 walker 从 .68.200:*临时端口* 发出的查询 + // 被误判为 Butler 事件转发给 SL,永远到不了总线。 + if from == butler_sa { + // Butler 广播/事件 → 所有客户端 + for up in ups.iter() { + let _ = a.send_to(&ba[..n], up.addr); + n_rev += 1; + } + } else { + match ups.iter_mut().find(|up| up.addr == from) { + Some(up) => { + let _ = up.sock.send_to(&ba[..n], &bdst); + n_fwd += 1; + } + None => { + let sock = open_reuse(&format!("{}:0", src_ip)); + let _ = sock.set_nonblocking(true); + let _ = sock.send_to(&ba[..n], &bdst); + ups.push(Up { addr: from, sock }); + if ups.len() > 8 { + ups.remove(0); + } + n_fwd += 1; + println!("[RELAY] 客户端 #{} = {}", ups.len(), from); + } + } } } if (n_fwd + n_rev) % 2000 == 1 && n_fwd > 1 { - println!("[RELAY] 已转发 客户端→Butler {} 帧, Butler→客户端 {} 帧", n_fwd, n_rev); + println!("[RELAY] 已转发 客户端→Butler {} 帧, Butler→客户端 {} 帧 ({} 客户端)", n_fwd, n_rev, ups.len()); } } }