64.TCP 可靠性与效率
2026/6/5 23:19:40 网站建设 项目流程

如果客户端先发送fin关闭文件描述符那就接受不了服务器发的fin,所以要保留读的文件描述符,shutdown,关闭读或者写或者读写,

当我们调用close,对方也调用close,如果还有数据,服务器也要保证把数据刷过去再调用close,调用close就行,操作系统完成。 这只是说有这样的接口。

四次挥手,小双方发消息完没完不一定是同时的,所以ack和fin不能捎带应答,所以就是四次挥手,有些情况双方都想断开,所以也可能三次挥手,握手挥手本质都是四次,因为他们各自特征决定的。

客户端关,服务器不关,服务器状态进入close wait 客户端状态一直是findwait2,

服务器端用完一个文件描述符,必须关,不关,就可能处于closewait,一直占用文件描述符,这也是资源,有限的,这叫文件描述符泄露问题,

客户端最后发出ack就算四次完成,而服务器要收到,所以客户端要等待一段时间,客户端金额入timewait状态

  • 客户端调用close()$\rightarrow$ 内核发送FIN(第一次挥手) $\rightarrow$ 服务端内核自动ACK(第二次挥手)。此时客户端已单方面闭门谢客。

  • 服务端应用处理完后手,也调用close()$\rightarrow$ 服务端内核发送FIN(第三次挥手) $\rightarrow$ 客户端内核自动ACK(第四次挥手)。此时连接彻底寿终正寝。

最大报文存活时间:一个报文从主机发送到网络,发了很多报文,有个统计数据,统计报文最大生存时间,

客服端给服务器发的消息,某个发送的消息判定超时了,超时重发,但是这个消息没丢,他还在某个路由器卡着,四次挥手做完了,但是这个消息还是活着。后来客户端关闭后立即再重启,端口一致,这个报文可能就直接到来了。就可能影响下次连接建立过程和数据通信过程。

1. 误区一:挥手花的时间 vs 迷路报文能活的时间

你把“正常的网络传输时间(RTT)”“报文的最大生存时间(MSL)”混淆了。

  • 四次挥手有多快?正常情况下,四次挥手的报文是在网络中顺畅跑的,一来一回(RTT)通常也就是几十毫秒(ms),即使慢一点也就几百毫秒。整个四次挥手做完,可能连 1 秒钟都不到。

  • MSL 有多长?MSL(Maximum Segment Lifetime)是报文在网络里能存活的极限时间。RFC 793 标准建议是 2 分钟(实际工程中,Linux 通常设定为 30 秒或 60 秒)。

发现差距了吗?即便四次挥手磨磨唧唧花了 1 秒钟,而那个卡在某个拥堵路由器里的“迷路旧报文”,它的寿命可是长达 60 秒!挥手所花的那点时间,根本不足以把这个幽灵报文给“熬死”。等四次挥手结束时,那个幽灵报文大概率还活蹦乱跳地在网络里找出路呢。

2. 误区二:为什么要等“2”个 MSL?

既然 1 个 MSL 就代表一个报文能活的最长时间,等 1 个 MSL 不就行了吗?为什么非要等 2 个?

这 2 个 MSL(比如 60秒 × 2 = 120秒)的等待期,也就是TIME_WAIT状态,其实承担了两个截然不同但都至关重要的保底任务

任务 A:防旧报文干扰(你提到的幽灵报文问题)

倒计时是从客户端发出第四次挥手(最后的 ACK)那一刻开始算的。 协议规定等 2MSL,是为了提供一个绝对安全的双倍缓冲期。既然网络中最极端的单向延迟是 1MSL,那么等待 2MSL 可以 100% 确保:在这个连接期间产生的任何报文,不管去程还是回程,不管迷路多久,在这 2MSL 的时间里,它们的 TTL(存活跳数)必定会被扣到 0,死在网络中。这样就能保证,当端口再次被使用时,网络管道里绝对干干净净。

任务 B:确保服务器能顺利“闭眼”(可靠的终止连接)

这是 2MSL 的另一个核心原因(也是为什么刚好是 2 的数学原因)。 试想一下四次挥手的最后一步:客户端发出了最后的 ACK,然后如果客户端直接跑路(关机/释放端口),会发生什么?万一这个最后的 ACK 迷路丢了呢?

  1. 服务器没收到 ACK,它会觉得:“客户端是不是没收到我的分手信(FIN)?”

  2. 于是,服务器会重新发送 FIN

  3. 如果此时客户端已经跑路了,服务器就会收到一个 RST 报错,服务器会非正常关闭。

为了防止这种情况,客户端发完最后一次 ACK 后,必须留在原地等2MSL

  • 去程 1MSL:留给最后那个 ACK 走到服务器的最长时间。

  • 回程 1MSL:如果 ACK 丢了,留给服务器重发 FIN 走到客户端的最长时间。

如果客户端在原地等了 2MSL,依然没有收到服务器重发的 FIN,客户端就可以极其自信地断定:“看来我最后发的 ACK 对方妥妥收到了,没人找我麻烦了,我可以安心彻底关闭了!”

主动断开一方一定是最后能接收数据的一方,所以要有等待时间。为了让历史残留报文在网络中消散,不会影响下一次网络连接和通信。等待期间来了报文直接丢弃。

虽然概率低,但是一旦有,tcp多设备多连接大概率每天都会实现。

主动断开连接一方要进入timewait,再次重启,并没有close,链接还在使用,端口号也在使用,再次绑定就失败。这样就可以对网络中残留的报文进行丢弃

如果最后一次ACK丢失那么对方一定,timewait期间没有在收到fin就证明对方收到ack, 较大概率保证握手是正常完成的。

这样即使服务器崩了,也能立即重启,但是网络中丢失报文再进来怎么办,通信时候,服务器做ack时候,我知道 客户端从哪发数据,如果data序号和期望不匹配也可以丢弃,双方握手时,双方会进行序号随机化,你从多少开始我从多少开始,就可以振别出来,还会根据缓冲区大小计算上界。

滑动窗口:

看课件7滑动窗口

快重传,如果收不到三个就用超时重传

收到ack,滑动窗口右滑就把数据丢弃掉,没有收到应答就不要滑动,这就是暂存。

最左侧丢了,1001-2000丢了,ack只能1001,滑动窗口不动,为了进行超时重传和快重传,

中间丢失,滑动窗口首先向右滑动到中间丢失的位置,因为根据确认序号的定义,是我已经收到的报文,就变成最左侧丢失

最右边丢了,也是向右滑动,在丢失部分停下来,转化为最左侧丢失

所有报文都触发超时重传,如果同时收到3个以上相同序号ack取消超时用快重传

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询