fix(tools): 探针区分信令回复与打洞包,修掉打洞假阳性
问题:客户端反复注册引来的 PEER/WAIT 回复在 discover_peer 返回后才到达,
被接收线程当成「对方打进来了」,输出假的「✅ 打洞成功」。
实测形态:报告的对方地址实为信令服务器 47.98.245.66:47999,包长 22 字节
(真打洞包固定 16 字节 = PUNCHv1| + 8 字节时间戳)。
修复:
- 接收线程只认以 MAGIC 开头、且长度正确的包
- 显式排除信令服务器的 IP:端口
- 失败结论里报告被忽略的非打洞包数量,避免再次误读
- 验证记录补 4.5 节,写明 Rust 实现的对应判据
This commit is contained in:
parent
2487e616c2
commit
18d12baba1
@ -174,8 +174,15 @@ class Probe:
|
||||
self.stop = threading.Event()
|
||||
self.first_src = None
|
||||
self.packets = 0
|
||||
self.junk = 0
|
||||
|
||||
def receiver(self):
|
||||
def receiver(self, ignore=()):
|
||||
"""接收线程。
|
||||
|
||||
只认「以 MAGIC 开头」且「来自对端而非信令服务器」的包。
|
||||
信令服务器的 PEER/WAIT 回复绝不能算作打通——否则客户端反复注册引来的
|
||||
回复会造成假阳性(已踩过:把服务器的 PEER 回复误判成打洞成功)。
|
||||
"""
|
||||
while not self.stop.is_set():
|
||||
try:
|
||||
data, src = self.sock.recvfrom(2048)
|
||||
@ -188,6 +195,12 @@ class Probe:
|
||||
if getattr(e, "winerror", None) == 10054:
|
||||
continue
|
||||
break
|
||||
|
||||
# ---- 过滤:非打洞包一律丢弃
|
||||
if not data.startswith(MAGIC) or (ignore and src[:2] in ignore):
|
||||
self.junk += 1
|
||||
continue
|
||||
|
||||
if self.first_src is None:
|
||||
self.first_src = src
|
||||
print("\n ✅ 收到对方的包!")
|
||||
@ -297,7 +310,13 @@ def main():
|
||||
peer = found
|
||||
print(" ✅ 配对成功,对方公网地址 = %s" % fmt_addr(found))
|
||||
|
||||
t = threading.Thread(target=probe.receiver, daemon=True)
|
||||
# 信令服务器的回复(PEER/WAIT)绝不能算作打通,接收线程必须把它排除掉
|
||||
ignore = set()
|
||||
if args.server:
|
||||
_sh, _sp, _sf = parse_addr(args.server)
|
||||
ignore.add((_sh, _sp))
|
||||
|
||||
t = threading.Thread(target=probe.receiver, args=(ignore,), daemon=True)
|
||||
t.start()
|
||||
|
||||
try:
|
||||
@ -328,7 +347,10 @@ def main():
|
||||
print(" 对方实际源地址 = %s" % fmt_addr(probe.first_src))
|
||||
print(" 共收到 %d 个包" % probe.packets)
|
||||
else:
|
||||
print("结论:❌ 本轮没有收到对方的包")
|
||||
print("结论:❌ 本轮没有收到对方的打洞包")
|
||||
if probe.junk:
|
||||
print(" (另外收到 %d 个非打洞包并已忽略:那是信令服务器的回复,不算打通)"
|
||||
% probe.junk)
|
||||
print(" 可能原因(按概率排序):")
|
||||
print(" 1. 一端或两端是对称型 NAT / 运营商级 NAT(CGNAT) → 需要端口预测或中继")
|
||||
print(" 2. 一方根本没发包(对方没启动、或地址填错/端口填错)")
|
||||
|
||||
12
打洞验证记录.md
12
打洞验证记录.md
@ -99,6 +99,18 @@ sock.ioctl(socket.SIO_UDP_CONNRESET, False) # 1) 关闭该行为
|
||||
|
||||
> 注意:首次配置时误把安全组规则设成了 **TCP**,UDP 包仍被丢弃。协议类型必须选 UDP。
|
||||
|
||||
### 4.5 假阳性:信令回复被误判为"打通"
|
||||
|
||||
客户端在拿到对端地址前会反复注册,服务器的 `PEER` / `WAIT` 回复可能在 `discover_peer` 返回之后才到达。若接收线程不做区分,会把**服务器发来的回复**当成"对方打进来了",输出假的"✅ 打洞成功"。
|
||||
|
||||
实测踩到的形态:报告的"对方源地址"是**信令服务器的 IP:端口**(`47.98.245.66:47999`),包长 22 字节——而真正的打洞包固定是 **16 字节**(`PUNCHv1|` + 8 字节时间戳)。
|
||||
|
||||
**判据(Rust 实现必须照做)**:
|
||||
|
||||
1. 只认以协议魔数开头的包,长度必须是约定值
|
||||
2. 显式排除信令服务器的 `IP:端口`
|
||||
3. 结论里如果出现"非打洞包 N 个",说明收到了信令回复,**不能**据此判定成功
|
||||
|
||||
---
|
||||
|
||||
## 5. 尚未验证
|
||||
|
||||
Loading…
Reference in New Issue
Block a user