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

2025 年 9 月 4 日
 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 就难以自圆其说

2877 次点击
所在节点    程序员
14 条回复
ysc3839
2025 年 9 月 4 日
NAT 类型检测错了吧,阿里云服务器怎么可能是 Port Restricted ?没有公网 IP 吗?
ysc3839
2025 年 9 月 4 日
还有一种可能是 Windows 机子用端口扫描的方式,往 Mac 机子 IP 的不同端口都发送数据包,遇到有回应的端口那就是 NAT 分配到的端口
ptg2008
2025 年 9 月 4 日
@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
2025 年 9 月 4 日
@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,
}
46fo
2025 年 9 月 4 日
估计类似于 生日悖论暴力扫描 https://arthurchiao.art/blog/how-nat-traversal-works-zh/
edcopclub
2025 年 9 月 4 日
在一定范围内扫描端口连接就行了,有概率能正好能连上。
psllll
2025 年 9 月 4 日
试试用 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
2025 年 9 月 4 日
@ptg2008 你把安全组所有端口都放通再试一下,我总觉得你这是被安全组拦了。
Domado
2025 年 9 月 5 日
我倾向于认为是 hard NAT ,因为过了 5 分钟才成功
Domado
2025 年 9 月 5 日
通过暴力扫描实现
le4tim
2025 年 9 月 15 日
我认为应该是 k1 + k2 < 8 时, A 和 B 能容易成功建立 p2p 连接,因为 k3+k4 可以使用生日悖论碰撞端口。
你可以试试 http://gonc.cc/ 这个工具在两端测试建立点对点连接,它会检测 nat 类型,打洞过程都有详细输出信息的,必要时就使用生日悖论碰撞端口,没有使用 relay 的。
ptg2008
2025 年 9 月 15 日
@le4tim 大佬 感谢你的回答 这两天我看了 easytier 的代码实现 我已经大概明白是 nat3 对 nat4 是怎么打洞的了
20starter
2025 年 9 月 16 日
打洞成功的流程应该是三步:
1.nat3 机器通过 stun 建立公网 ip 端口映射
2.nat4 机器尝试访问 nat3 机器(会失败),为了得到对 nat3 机器的公网 ip 端口映射
3.nat3 机器再次访问 nat4 机器,这时两边应该就能通了
楼主你看的 easytier 代码实现原理是咋样的,我最近做了个在线 nat 类型检测网站 https://natchecker.com ,想看看自己真学会了没。
ptg2008
2025 年 9 月 16 日
@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