大佬们, 请教一个 udp 建立 p2p 连接的问题

47 天前
 ptg2008

前置科普

nat 类型分类

结论: 若 Full Cone = 1, Restricted Cone = 2, Port Restricted Cone = 3, Symmetric = 4, 假设 A 端的 nat 类型代表的数值为 k1 (1 <= k1 <= 4), B 端的 nat 类型代表的数值为 k2 (1 <= k2 <= 4), 根据我掌握的到的计算机网络知识, 只有当 k1 + k2 <= 6 时, A 和 B 才能成功建立 p2p 连接

发现问题

今天我试了用 easytier 组网, 具体来说, 就是我有台的 MacBook Pro, 一台 Windows, 还有一个阿里云小水管机器, 组网成功后, 我执行了命令查看组网情况, 如下图所示 没错, 符合我预期, MacBook Pro 是 Symmetric = 4, Windows 是 Port Restricted Cone = 3, 4 + 3 = 7 > 6, 只能走阿里云小水管中继

但是 5 分钟后我又执行了命令查看组网情况, 如下图所示 发现了 MacBook Pro 和 Windows 之间是 变成了 P2P 连接

分析一波

网络拓扑大概这样

MacBook (priv_ip1, priv_port1)
    |
    |
公网出口 1 (pub_ip1, pub_port1)  
    |
   ...(中间很多路由)
    |
阿里云小水管 (aliyun_ip, aliyun_port)
    |
   ...(中间很多路由)
    |
公网出口 2 (pub_ip2, pub_port2) 
    |
    |
Windows (priv_ip2, priv_port2)    

分析如下

* case1: 
 1. Windows 发起请求访问 MacBook 的(pub_ip1, pub_port1) , 但是被拒绝, 这个请求对于 nat4 而言是"陌生请求"(因为 Windows 和 MacBook 之前没有建立连接)
 2. 但是这时候能从(pub_ip1, pub_port1)访问 Windows 了 (因为是 nat3, 端口和 IP 都受限, 只能从(pub_ip1, pub_port1)访问, 其它的网段访问 Windows 还是会被拒绝)
 3. 这时候 MacBook 再次向(pub_ip2, pub_port2)发起请求, 但是因为 MacBook 是 nat4, 导致分配到的出口路由器的端口不再是 pub_port1, 而是 another_pub_port1 (假设出口路由器不变)
 4.  这样访问 Windows 就会被拒绝, 因为对于 Windows 而言, 只允许从(pub_ip1, pub_port1)过来的请求访问, 而从(pub_ip1, another_pub_port1)过来的是没法访问的

* case2: 
 1. MacBook 发起请求访问 Windows 的(pub_ip2, pub_port2) , 但是被拒绝, 因为是 nat4, 导致分配到的出口路由器的端口不再是 pub_port1, 而是 another_pub_port1 (假设出口路由器不变)

所以这样就没法打洞成功

疑问

AI 怎么说

所有 AI 不能很清楚的回答我的问题, 在我的追问后这些 AI 就难以自圆其说

1939 次点击
所在节点    程序员
14 条回复
ysc3839
47 天前
NAT 类型检测错了吧,阿里云服务器怎么可能是 Port Restricted ?没有公网 IP 吗?
ysc3839
47 天前
还有一种可能是 Windows 机子用端口扫描的方式,往 Mac 机子 IP 的不同端口都发送数据包,遇到有回应的端口那就是 NAT 分配到的端口
ptg2008
47 天前
@ysc3839 这个我考虑过在 case1 的第 4 步后 Mac 从(pub_ip1, another_pub_port1)访问 windows 的(pub_ip2, pub_port2) 会被拒绝, 但是对于(pub_ip1, another_pub_port1)而言, 可以从 windows 的(pub_ip2, pub_port2) 访问, 因为由 Mac 主动发起了, 不过这个 another_pub_port1 只能靠对端的 windows 去猜 another_pub_port1 的分配规律, 猜中的话就能建立接连, 但是我 rust 不熟练, 等我有空了我看看 easytier 的实现
ptg2008
47 天前
@ysc3839 检测了 他确实是 Port Restricted, 有公网 IP 的

Welcome to Alibaba Cloud Elastic Compute Service !

Last login: Thu Sep 4 15:06:12 2025 from 58.251.160.132
➜ ~ sudo /opt/easytier/easytier-cli stun
stun info: StunInfo {
udp_nat_type: PortRestricted,
tcp_nat_type: Unknown,
last_update_time: 1756978903,
public_ip: [
xxxxxx,
],
min_port: 58939,
max_port: 58939,
}
xdeng
47 天前
估计类似于 生日悖论暴力扫描 https://arthurchiao.art/blog/how-nat-traversal-works-zh/
edcopclub
47 天前
在一定范围内扫描端口连接就行了,有概率能正好能连上。
psllll
47 天前
试试用 frp 的 xtcp 模式
可以一直重试直到打洞成功,不会回落走中转
像这样
serverAddr = ""
serverPort =
natHoleStunServer = "stun.miwifi.com:3478"


[[visitors]]
name = ""
type = "xtcp"
keepTunnelOpen = true
maxRetriesAnHour = 3600
minRetryInterval = 1
serverName = ""
bindAddr = "127.0.0.1"
bindPort = 10088
arrow629
46 天前
@ptg2008 你把安全组所有端口都放通再试一下,我总觉得你这是被安全组拦了。
Domado
46 天前
我倾向于认为是 hard NAT ,因为过了 5 分钟才成功
Domado
46 天前
通过暴力扫描实现
le4tim
36 天前
我认为应该是 k1 + k2 < 8 时, A 和 B 能容易成功建立 p2p 连接,因为 k3+k4 可以使用生日悖论碰撞端口。
你可以试试 http://gonc.cc/ 这个工具在两端测试建立点对点连接,它会检测 nat 类型,打洞过程都有详细输出信息的,必要时就使用生日悖论碰撞端口,没有使用 relay 的。
ptg2008
36 天前
@le4tim 大佬 感谢你的回答 这两天我看了 easytier 的代码实现 我已经大概明白是 nat3 对 nat4 是怎么打洞的了
20starter
35 天前
打洞成功的流程应该是三步:
1.nat3 机器通过 stun 建立公网 ip 端口映射
2.nat4 机器尝试访问 nat3 机器(会失败),为了得到对 nat3 机器的公网 ip 端口映射
3.nat3 机器再次访问 nat4 机器,这时两边应该就能通了
楼主你看的 easytier 代码实现原理是咋样的,我最近做了个在线 nat 类型检测网站 https://natchecker.com ,想看看自己真学会了没。
ptg2008
34 天前
@20starter 大佬 谢谢的你回答 easytier 的代码我用 ai 辅助我理解的, 你也可以尝试用 AI 了解实现原理, 我也只是懂个大概, 不敢细说原理免得误人子弟了

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/1157128

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX