news 2026/4/16 8:36:22

TCP(2)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
TCP(2)

一、第一次握手丢失会怎么样?

1. 触发超时重传

当客户端发送 SYN 报文后,会进入SYN_SENT状态。此时客户端会启动一个重传定时器。如果在规定时间内没有收到服务端的确认(SYN-ACK),客户端就会认为报文丢失,从而重新发送 SYN 报文。

2. 重传的时间间隔(指数退避)

TCP 不会无限制地立即重传,而是采用指数退避算法。在大多数操作系统(如 Linux)中,重传的策略通常如下:

  • 第一次重传:在 1 秒后(通常初始 RTO 为 1s)。

  • 第二次重传:如果还没收到,等待 2 秒后重传。

  • 后续重传:等待时间翻倍(4s, 8s, 16s...)。

3. 最大重传次数

重传并不会永远持续下去。在 Linux 系统中,受内核参数tcp_syn_retries的限制。

  • 默认值通常为5 次6 次

  • 如果达到最大次数后依然没有收到响应,客户端就会放弃连接,并向应用层返回“连接超时”的错误。

4. 整体等待时间计算

假设tcp_syn_retries为 5 次,重传序列通常是: 1s + 2s + 4s + 8s + 16s + 32s =63秒。 这意味着,如果第一次握手丢失且服务端一直没响应,用户可能需要等待一分钟左右才会看到最终的报错。

二、第二次握手丢失会怎么样?

1. 双重重传机制

由于第二次握手(SYN-ACK)具有“承上启下”的作用,它既是对客户端 SYN 的确认,也是服务端发起的连接请求。如果它丢失了:

  • 客户端(认为自己的 SYN 没传到):客户端迟迟收不到确认,会触发重传,再次发送SYN报文。

  • 服务端(认为自己的 SYN-ACK 没传到):服务端发送 SYN-ACK 后会进入SYN_RCVD状态。由于没有收到客户端的 ACK(第三次握手),服务端也会触发超时重传,再次发送SYN-ACK报文。

2. 重传次数与参数控制

两端的重传逻辑由不同的内核参数控制:

角色动作控制参数默认值 (Linux)
客户端重传SYNtcp_syn_retries通常为 5-6 次
服务端重传SYN-ACKtcp_syn_retries_2(或tcp_synack_retries)通常为 5 次

3. 指数退避与超时

和第一次握手类似,两端都会遵循指数退避算法(1s, 2s, 4s...)。

  • 最终结果:如果网络一直不通,两端都会在达到最大重传次数后放弃。

  • 状态清理:服务端在放弃前,会维持一个半连接(Half-open connection)。如果丢失过多,可能会填满服务端的SYN 队列,导致所谓的“SYN Flood”攻击效果,使正常用户无法建立连接。


4. 关键区别:谁先“放弃”?

在实际环境中,客户端和服务端的重传次数设置可能不同。

  • 如果服务端先达到上限,它会关闭半连接并释放资源。

  • 如果客户端先达到上限,它会直接报错给应用程序(如 Browser 报错连接超时)。

总结

第二次握手丢失是最浪费资源的情况,因为它迫使通信双方都在不断尝试重发。

三、第三次握手丢失会怎么样?

1. 双方状态的“不对等”

当第三次握手丢失时,客户端和服务端处于完全不同的认知状态:

  • 客户端(Client):在发送完 ACK 后,状态立即变为ESTABLISHED。它认为连接已经建立,可以开始发送数据了。

  • 服务端(Server):没收到 ACK,状态依然维持在SYN_RECV。它认为握手还没完成。


2. 服务端的反应:超时重传

服务端因为没有收到最后的确认包,会触发超时重传机制

  1. 重发 SYN+ACK:服务端会认为自己之前的第二次握手包丢了,于是重新发送SYN+ACK给客户端。

  2. 次数限制:服务端会尝试多次(通常是 5 次,取决于系统设置),每次间隔时间倍增(1s, 2s, 4s...)。

  3. 关闭连接:如果重传达到上限依然没有收到客户端的 ACK,服务端会自动关闭这个连接并回收资源。


3. 客户端的反应:视情况而定

客户端的后续表现取决于它是否立即发送数据:

  • 场景 A:客户端只握手,不发数据客户端处于ESTABLISHED,但在收到服务端重传的SYN+ACK时,会意识到之前的 ACK 可能丢了,于是再次发送 ACK给服务端。如果这次送达了,双方都进入正常状态。

  • 场景 B:客户端立即发送数据包

    • 客户端认为连接已通,直接发送携带载荷(Payload)的数据包。

    • 关键点:在 TCP 协议中,数据包本身也带有 ACK 确认信息

    • 当服务端收到这个“带数据的包”时,它能从包头发现 ACK 号是正确的,于是直接从SYN_RECV转为ESTABLISHED状态,并正常处理数据。这种情况下,丢失的第三次握手被后续的数据包“补偿”了。

  • 场景 C:连接被服务端关闭后,客户端才发数据如果服务端重传多次失败已经关闭了连接,而客户端此时才发送数据。服务端会因为找不到对应的连接记录,直接回执一个RST(重置)包,告诉客户端:“我这没这个连接,请重连。”

四、第一次挥手丢失了会怎么样?

1. 客户端:进入超时重传

当客户端发送 FIN 包后,状态会由ESTABLISHED变为FIN_WAIT_1

  • 等待 ACK:客户端会等待服务端的确认(ACK)。如果由于网络原因 FIN 包丢失,客户端迟迟收不到 ACK。

  • 超时重传:客户端会触发超时重传(Retransmission)机制,重新发送这个 FIN 包。

  • 退避算法:重传的时间间隔通常会翻倍(例如 1s, 2s, 4s...),以减轻网络拥堵。

  • 重传次数限制:客户端会重传若干次(由内核参数tcp_orphan_retries控制,通常为 5-8 次)。


2. 服务端:毫无察觉,保持连接

由于第一次挥手(FIN)压根没到服务端,服务端并不知道对方想要断开连接:

  • 状态不变:服务端依然维持在ESTABLISHED状态。

  • 继续等待:服务端会认为连接依然正常,继续等待接收数据或发送数据。


3. 最终结局:静默关闭 或 RST

如果客户端重传多次 FIN 后依然没有任何回应(服务端宕机或网络彻底中断),客户端将不再尝试:

  1. 客户端直接关闭:客户端达到最大重传次数后,会强制释放连接资源,直接进入CLOSED状态。

  2. 服务端超时释放(Keepalive):* 此时服务端还傻傻地开着连接。

    • 如果服务端开启了TCP Keepalive(保活机制),在一段很长的时间(默认通常是 2 小时)没有数据往来后,服务端会发送探测包。

    • 因为客户端已经关闭了连接,客户端可能会回一个RST(重置)包

    • 服务端收到 RST 后,也会意识到连接已断开,从而释放资源。


总结

阶段现象结果
客户端 (Client)状态变为FIN_WAIT_1,没收到 ACK超时重传FIN 包;若多次重传失败,直接强制关闭
服务端 (Server)没收到 FIN 包状态保持ESTABLISHED;直到保活探测超时或收到 RST 后才关闭。

简单来说:第一次挥手丢失并不可怕,客户端会通过“反复横跳”(重传)来尝试修复;如果实在连不上,客户端会自己先“撒手不管”(直接关闭),而服务端最终也会因为超时而释放资源。

五、第二次挥手丢失了会怎么样?

1. 双方状态的“信息差”

  • 客户端(主动关闭方):发送了 FIN,正在FIN_WAIT_1状态死等 ACK。

  • 服务端(被动关闭方):收到了 FIN,已经回了 ACK,状态变为CLOSE_WAIT。它认为自己已经告诉客户端“我知道你要断开了”,正在处理剩下的任务(比如发完最后的缓冲区数据)。


2. 会发生什么?(双重重传机制)

由于 ACK 报文本身是没有确认机制的(即 ACK 包丢了,接收方不会回一个 ACK 来确认收到了 ACK),系统只能靠“超时”来解决问题:

A. 客户端:重传 FIN

客户端在FIN_WAIT_1状态下,如果收不到 ACK,它会认为自己的第一次挥手(FIN)丢了。

  • 动作:客户端会再次发送 FIN 包。

  • 后果:只要服务端还活着,它每收到一个重传的 FIN,就会再次触发一个第二次挥手(ACK)。

B. 服务端:等待应用层指令

服务端此时处于CLOSE_WAIT状态。这个状态很特殊,它在等待应用程序调用close()函数来发送第三次挥手(FIN)。

  • 如果服务端应用程序处理得很快,在客户端重传 FIN 之前就发出了第三次挥手(FIN),那么这个新的 FIN 包其实兼具了 ACK 的功能

  • 客户端收到这个 FIN 后,会意识到连接已经可以进入下一阶段。


3. 最极端的有趣情况:如果客户端一直收不到 ACK

如果网络环境极差,导致所有的 ACK 都丢了,或者服务端处理极慢:

  1. 客户端心灰意冷:客户端在重传多次 FIN 失败后,会直接认为连接已死,强行关闭(进入CLOSED)。

  2. 服务端“守活寡”:服务端可能还停留在CLOSE_WAIT状态,等待它自己的应用程序关闭。

    • 如果应用程序不调用close(),这个CLOSE_WAIT永久存在

    • 这就是为什么在排查服务器问题时,如果看到大量CLOSE_WAIT,通常意味着程序代码有 Bug,忘记关闭连接了。


4. 总结对比

视角认知反应
客户端“我的 FIN 丢了吧?怎么没回音?”重传 FIN,直到收到 ACK 或达到重传上限。
服务端“我已经答应要关了,等我忙完。”每次收到重传的 FIN,就补发一个 ACK

有趣的一点是:

如果第二次挥手(ACK)丢失,但紧接着第三次挥手(FIN)发过来了,客户端可以直接从 FIN_WAIT_1 跳过中间状态,直接进入确认阶段。TCP 协议的设计非常“务实”,只要目的达到了,过程中的小波折可以通过重传来修补。

六、第三次挥手丢失了会怎么样?

1. 服务端的反应:它是真的“着急”

在第三次挥手(服务端发送 FIN)时,服务端的状态由CLOSE_WAIT转为LAST_ACK

  • 重传责任:因为这是一个带有FIN标志的报文,它必须得到客户端的确认(第四次挥手 ACK)。

  • 动作:服务端如果收不到第四次挥手的 ACK,它会认为自己的 FIN 包丢了。

  • 后果:服务端会开启超时重传,反复发送这个 FIN 包。

  • 结局:如果重传多次(由tcp_orphan_retries参数决定)依然无果,服务端会彻底死心,强制进入CLOSED状态并释放内存资源。


2. 客户端的反应:它是真的“无助”

客户端在收到第二次挥手(ACK)后,状态进入了FIN_WAIT_2

  • 死等的困境:在这个状态下,客户端已经完成了它的关闭任务,它在等待服务端的 FIN 包。

  • 没有重传权:客户端此时是“接收方”,它没法主动发什么东西来催促服务端,只能干等。

  • 超时机制:* 如果客户端是通过调用close()关闭的(这种连接叫孤儿连接,Orphan Socket),内核会给FIN_WAIT_2设置一个定时器(通常由tcp_fin_timeout参数决定,默认 60s)。

    • 如果 60s 内没收到 FIN 包,客户端会直接断开连接。


3. 谁先“着急”?(场景推演)

通常情况下,服务端会先表现出“着急”的状态,因为它在不断地重传 FIN 包:

  • 如果网络恢复:服务端重传的 FIN 包终于到了客户端。客户端回一个 ACK(第四次挥手),双方圆满结束。

  • 如果网络彻底断掉:

    • 客户端:盯着表看,60 秒一到,准时走人(进入CLOSED),释放资源。

    • 服务端:还在那儿一次又一次地重传 FIN,直到达到最大重传次数(可能需要几分钟),才最终放弃。


4. 总结对比

角色状态心理活动最终结局
服务端 (Server)LAST_ACK“我把最后的话 (FIN) 发了,对方怎么不理我?重发一次试试!”多次重传,最后实在没反应就强制关闭。
客户端 (Client)FIN_WAIT_2“我等着听他最后一句告别,等不到我可就走了。”静默等待,超时(通常 60s)后直接闪人。

七、第四次挥手丢失会怎么样?

1. 服务端的反应:我认为你没收到

服务端发送了第三次挥手(FIN)后,进入了LAST_ACK状态。

  • 没收到回应:服务端在一段时间内没收到客户端回传的 ACK,它会认为:“糟了,我的 FIN 包可能丢了,或者客户端没收到。”

  • 动作:服务端会重传第三次挥手(FIN)

  • 循环:只要没收到 ACK,服务端就会一直重传 FIN,直到达到最大重传次数后强行关闭。


2. 客户端的反应:我得等一会儿再走

客户端在发送完第四次挥手(ACK)后,并没有立即进入CLOSED状态,而是进入了著名的TIME_WAIT状态。

  • 收到重传的 FIN:客户端在TIME_WAIT期间,如果收到了服务端重传的 FIN 包,它会意识到:“看来我刚才发的 ACK 丢了。”

  • 动作:客户端会再次发送 ACK,并重新启动TIME_WAIT定时器。


3. 核心机制:为什么要等待 2MSL?

客户端在TIME_WAIT状态下需要等待2MSL(Maximum Segment Lifetime,报文最大生存时间,通常是 1 到 2 分钟)才会进入CLOSED。这样做有两个核心目的:

原因一:确保可靠地关闭连接

如上所述,为了保证服务端能收到最后的 ACK。如果客户端发完 ACK 就直接闪人(进入CLOSED),而 ACK 恰好丢了,服务端重传 FIN 时,客户端已经不在了。此时客户端的内核会回一个RST包,服务端收到后会认为连接出了异常(报错),而不是正常的“优雅关闭”。

原因二:防止“已过时”的数据包捣乱

网络中可能存在因为绕路而“迟到”的旧数据包。

  • 等待 2MSL 可以保证在这个连接中产生的所有报文都在网络中消失。

  • 如果没有这个等待时间,客户端立即用相同的端口号开启一个新连接,那些迟到的旧报文可能会被误认为是新连接的数据,导致数据乱序或逻辑错误。


4. 总结:如果第四次挥手真的丢了

角色状态变化结果
服务端 (Server)LAST_ACK重传 FIN只有收到 ACK 后才会进入CLOSED
客户端 (Client)TIME_WAIT只要收到重传的 FIN,就不断补发 ACK,直到 2MSL 定时器耗尽。

趣味思考:

虽然TIME_WAIT保证了可靠性,但在高并发服务器(如爬虫或大量短连接服务)上,大量的TIME_WAIT会占用掉所有可用的端口号,导致无法建立新连接。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/16 8:35:31

朱雀AI检测不通过怎么办?这3款降AI工具亲测有效

朱雀AI检测不通过怎么办?这3款降AI工具亲测有效 “论文改了三遍,朱雀AI检测还是不通过。” 这是上周室友发在群里的一句话,当时她的论文AI率卡在38%,学校要求控制在20%以下。她已经手动改写了两天,AI率从最初的55%降到…

作者头像 李华
网站建设 2026/4/16 8:29:41

项目介绍 MATLAB实现基于双向长短期记忆网络(BiLSTM)进行锂电池剩余寿命(RUL)预测(含模型描述及部分示例代码)专栏近期有大量优惠 还请多多点一下关注 加油 谢谢 你的鼓励是我前行的动力

MATLAB实现基于双向长短期记忆网络(BiLSTM)进行锂电池剩余寿命(RUL)预测的详细项目实例 请注意此篇内容只是一个项目介绍 更多详细内容可直接联系博主本人 或者访问对应标题的完整博客或者文档下载页面(含完整的程序…

作者头像 李华
网站建设 2026/4/16 8:28:38

抖音无水印下载器:从单视频到批量下载的完整解决方案

抖音无水印下载器:从单视频到批量下载的完整解决方案 【免费下载链接】douyin-downloader A practical Douyin downloader for both single-item and profile batch downloads, with progress display, retries, SQLite deduplication, and browser fallback suppor…

作者头像 李华
网站建设 2026/4/16 8:28:16

DeepSeek-OCR-2功能体验:双列可视化界面,左上传右预览操作直观

DeepSeek-OCR-2功能体验:双列可视化界面,左上传右预览操作直观 1. 产品核心价值解析 DeepSeek-OCR-2智能文档解析工具代表了新一代OCR技术的突破性进展。与传统OCR工具相比,它最大的差异化优势在于能够完整保留文档的结构化信息&#xff0c…

作者头像 李华
网站建设 2026/4/16 8:27:24

三月七小助手:如何用开源工具实现游戏自动化,每天节省90%时间

三月七小助手:如何用开源工具实现游戏自动化,每天节省90%时间 【免费下载链接】March7thAssistant 崩坏:星穹铁道全自动 三月七小助手 项目地址: https://gitcode.com/gh_mirrors/ma/March7thAssistant 你是否厌倦了游戏中的重复劳动&…

作者头像 李华