365bet网页版

TCP打洞,实现P2P(附Node源码)

发布时间 2025-11-22 22:57:03 作者 admin 阅读 286

前言之前有过一篇关于 NAT打洞 的,主要是 UDP打洞,感兴趣的可以看之前的文章:NAT打洞该篇文章主要讲述的是对 TCP打洞 的实现,以及其中的步骤的讲解和优化也算是继续完善这个系列的坑…

可运行的代码放在了 Github仓库

当你开始阅读时,默认读者已具备一定的网络基础以及对NAT有基本的认识那我们开始!

TCP 打洞与 UDP 的区别TCP与UDP打洞,最大的不同在于TCP是有连接,UDP是无连接这就决定了,UDP可以一边打洞一边监听端口其次就是,UDP打洞成功就能进行通信,TCP是要想办法达成握手,三次握手成了才可以通信

但其实,并没什么影响。。。因为打洞的原理并没有变,只是流程上需要稍微改变一下

流程整个的原理和流程如下图[^1]:(先说完这个图再说改进)

Step1-2: R 向服务器注册信息(这里其实UDP和TCP都可以,改进部分有说明)

Step3: I 向服务器发出 “想要连接的请求”,用的是TCP(这样可以知道 I 自身的 NAT 信息 NI:Y)

Step4: 服务器将 “I 想要连接的请求” 发送给 R

Step5: R 收到服务器发来的 “I 想要连接的请求” 后,用 R:J 向服务器发起 TCP 连接 服务器收到连接后,会收到来自 NR:K 的信息,之后会判断 NR 是否等于 R,

是的话,即 R 不经过 NAT,I 可以直接发起连接

否的话,即 R 经过 NAT,需要 R 打洞后,I 才可以发起连接(这里讨论这种情况)

Step6: 服务器向 R 发送打洞的消息,并将 NI:Y 信息发送给 R,表示可以通过 NI:Y 与 I 通信

Step7: R 收到 NI:Y 后,用 R:J 向 NI:Y 发送 TCP 连接请求(指SYN)

Step8: R 不会收到返回信息(指ACK),但这里也有可能收到拒绝信息,但这里不影响 随后 R 直接断开通过 R:J 发起的 TCP 连接,并开启 TCP Server 监听 J 端口

Step9: R 用 其他端口 向服务器发送 “打洞完成” 的信息

Step10: 服务器通过 NI:Y 将 “打洞完成” 的信息 以及 NR:K 的信息发送给 I,并且关闭与之的通信

Step11: I 收到信息后,会终止 NI:Y 与服务器的 TCP 连接,然后会用 NI:Y 向 NR:K 发起 TCP 连接请求(指SYN)

最终 SYN 会被 NR:K 送进给 R:J,之后会进行完整的三次握手建立TCP连接

改进这里的 Step1、2、9 其实对连接并没有要求1 2 9 可以用同一个连接 专门用来发送信息 也是可以的,这里起到的作用单纯是通知状态和交换信息甚至 Step1-2 直接用 R:J 与服务器进行通信也是可以的,因为 Step6 之后都会断开与服务器的连接而转为打洞和监听

其次,这里并没有那么严谨的区分 I 和 R 与服务器发起连接的顺序,因为与服务器建立连接之后,不管是 Step4 还是 Step10,都是在等待服务器返回信息 才进行下一步操作(这里代码中我用了心跳包的方式维持)

Step6 的 NI:Y 信息其实可以在 Step4 就带过来给 R,这里主要是考虑是否需要考虑打洞的情况,因为 R 如果不需要打洞的话 NI:Y 的信息其实并不需要告知 R但如果单纯是考虑是否需要打洞的话,其实还得考虑双方的 NAT 类型 来进一步判断是谁来打洞比如说:

I 是 NAT3 *端口限制性锥形NAT( Port Restricted Clone NAT)*,R 是 NAT4 对称式NAT(Symmetric NAT)则需要 I 来打洞,R 向 NI 发起连接

如果不需要考虑的话,其实这里 punchHole 一直为 True 都可以

改进后:(其实也没多大必要)sequenceDiagram

participant I

participant NI

participant S

participant NR

participant R

par

R ->>+ S : Step2: TCP - S:Z < NR:K < R:J

Note over NR: NR:K;想要被 I 连接

I ->> S : Step1: TCP - I:X > NI:Y > S:Z

Note over NI: NI:Y;想要连接R

end

Note right of S: NI:Y;需要打洞

S -->> R :

R->>+ NI: Step3: TCP - NI:Y < NR:K < R:J (打洞)

NI--x R:

# Note right of R: Destroy TCP R:J

R->>R: Step4: ① Destroy TCP R:J② CreateServer Listen R:J

R->>S: Step5: New Connection S:* < NR:* < R:*

Note over NR: 打洞完成

Note left of S: NR:K

S-->>- I:

I->>I: Step6: Destroy TCP I:X

I->>NI: Step7: TCP - I:X

NI->>-NR:

NR->>R:

Note over NI,NR: Step7: TCP - I:X > NI:Y > NR:K > R:J

R-->>I: ACK

I-->>R: ACK

par 里的 Step1、Step2 不分先后

Step3 发出去就可以进入 Step4 了

Step5 的 * 号表示任意,只要不是 R:J 都行(反正你也开Server监听了,也绑定不了)

Step7 是用 Step6 的 I:X 向 NR:K 直接发起连接

代码部分说明1231. 使用前,请先将 Server.js 部署,记得开启端口2. 完善 Client*.js 中的 `SERVER_IP` `SERVER_PORT` `LOCAL_PORT`3. 启动 ClientA.js 和 ClientB.js

1234567891011/** * 这里的 from 和 to,分别代表的是: * from: 想要发起连接的一方 * to: 被连接的一方 * * 它 **并不关乎** 这个 msg 是由 ClientA 发出还是由 ClientB 发出*/ msg: { from: "ClientA", to: "ClientB"}

123456789// 连接成功后,可以将 ClientA.js、ClientB.js 中的这段代码放开,然后通过键盘输入发送消息// 键盘输入发送消息process.stdin.on('data', data => { socket.write(JSON.stringify({ type: "msg", msg: data.toString().trim() }))})

其他一些实验发现

NAT 维持 TCP洞口 的时间一般比较短,我试过我这边区域的网络,一般是维持 5s 左右,超过 5s 这个端口没有数据包, NAT 会默认这个端口不再使用,外面数据包再想进来都会被 NAT 丢弃,这点不管是打洞,亦或是已经建立的TCP通信 (通过服务端设置延迟返回信息,测试得到,但各区域的 NAT 会有所不同,可以自己试下)

同样,在这段维持时间内,用户通过 同一个端口 向外发送信息,都会绑定同一个 NAT 端口。所以并不需要端口复用,只要在维持时间内,客户端 Destroy 之前的TCP连接(再暴力点可以直接抛出来终结掉之前的连接),再用 同一个端口 建立连接/监听这个端口 都是可以

数据包发送是毫秒级的,对于 5s 的窗口期来说 是完全足够的 (手动才勉强)

当前这个,最差可以在 NAT3 和 NAT3 之间建立起连接,如果其中一方是 NAT4 的话,NAT3 作为需要打洞的一方即可 但可能需要用 同一个端口 向 NAT4 的多个端口发送数据包(端口预测)

说在最后首先是发现,网上关于 “NAT穿越”、”打洞”、“P2P”…… 等等话题,要么就是一大段理论概念,要么就是水文章,实际有用的、能参考的没多少

剩下能参考的,基本都是 UDP 的,TCP 就几乎没有

然后 TCP 吧,有的基本都是 C/C++ 的然后还一堆不要脸 CV 的,要点脸的都知道挂个转载或者挂个出处吧特别是那些有这个字段 SO_REUSEADDR ,还跟你说一定要设置端口复用的,一大半都是 CV 的( 我就这么直接下定义了!)

然后这篇文章也算是补充了一下 我之前 NAT打洞 一文里 关于 TCP 的部分当时刚接触 然后去做相关的测试和实验,还比较的懵懂,然后是找了些资料 又再去研究了下 计算机网络 ,然后发现网上的 真的 好水呀,真的好气!然后还是决定去翻了翻书本和找了下文献之类的,现在过来填个坑吧

这同时也是完善 P2P 这部分的坑,后续其实还有就是 NAT3 / NAT4 与 NAT4 相互连接,NAT4 一般是 手机热点 连接的网络NAT3/4 与 NAT4 建立连接,需要 端口预测 / 端口探测,先挖个坑,后续有时间再来填…

参考文献[^1]: TCP Connections for P2P Apps: A Software Approach to Solving the NAT Problem

相关推荐

人民日报

古香榧树环绕着村庄。 诸暨市委宣传部供图 榧农在采摘香榧果实。 张捷珍摄(人民视觉) 香榧果实。 诸暨市委宣传部供图 香榧 香榧为红豆杉

08-16 分类 365bet网页版

消防疏散图

使用免费的示例和模板创建消防疏散图。创建消防疏散图变得前所未有的容易! 消防疏散图能够帮助人们摆脱威胁或者避免灾难的发生。亿图消

07-10 分类 best365官网登录入口

《DNF》决斗等级有哪些 决斗等级区分详解

导 读 DNF决斗场的决斗等级介绍,今天小编给大家带来的就是决斗场相关内容介绍,有不了解相关内容的小伙伴不要错过呦。 删除了段位分,新

11-03 分类 best365官网登录入口

小米手机官方网站的客服电话是多少?

手机官方网站的客服电话是400-100-5678,该热线提供24小时全天候服务,用户可随时拨打咨询产品使用、购买信息、售后服务等相关问题,以下是

07-10 分类 365bet官网是多少