CP三次握手和四次挥手,是面试官最爱问的“开场白”之一

别看它基础,真要讲清楚细节,分分钟让你冷汗直流!

这玩意儿就跟程序员相亲一样:

表面上问的是“你老家哪的”

实际上是在试探你有没有房、有没有车、能不能落户!

很多同学一开始答得头头是道

结果面试官一连追问:“为啥不是两次握手?”、“TIME_WAIT干啥的?”、“为啥挥手要四次?”

很多小伙伴在面试中可能会这样答:

“三次握手就是客户端和服务端互相确认连接状态的过程,四次挥手则是断开连接时双方分别关闭各自的发送通道。”

答案对吗?当然对!

但距离面试官的期望,可能还差那么一点点……

记住:简单的回答只是开始,深入的探讨才是关键。

其中有较多的细节问题,本篇文章全部会详细讲解!

接下来,希望大家能带着思考进行阅读,收获会更大。

Part1什么是TCP协议

你可以把它理解成互联网世界里的“老司机”

专治各种不可靠网络环境下的数据传输问题

它的口号是:“我不光要传得出去,还得收得回来!

简单来说,TCP 是一种 面向连接、可靠传输 的协议

它存在的意义,就是为了在那些“信号差、丢包多、谁都不靠谱”的网络环境下,给我们提供一条稳定、有序、不丢包、不乱序的数据传输高速公路。

Part2那TCP中的面向连接呢?

就是说,在真正开始传数据之前

客户端和服务器必须先坐下来“握个手”,确认彼此都在线、都能听懂对方说话

不能像发短信一样发完就走,也不管对方有没有收到。

就像相亲前先加个微信聊两句

总比直接上门提亲强吧?

Part3可靠传输?怎么保证?

  • 数据是按顺序传送的,不会前面的还没到,后面的先来了;
  • 每次传数据都会做校验,要是数据在路上被“压碎”了,接收方立马能发现;
  • 如果发现丢了包、错乱了,TCP 就会自动重传、排序,直到你满意为止。

总之,TCP 的原则是:宁可慢一点,也不能错一点!

Part4那三次握手又是干嘛的?

为了建立这个“稳如老狗”的连接

TCP 设计了一个流程叫 三次握手

目的很简单:确认双方的发送和接收能力都没毛病!

第一次握手:客户端 → 服务端

客户端主动发起请求,发送一个 SYN=1 的报文(同步标志),告诉服务端:“我想和你建立连接。”

同时指定了自己的初始序列号 seq=x。
这个 SYN 报文不能携带数据,但它会消耗一个序号。

此时客户端进入 SYN_SENT 状态,
心里默念:“我在等你回消息……”


第二次握手:服务端 → 客户端

服务端收到 SYN 后,表示收到了连接请求,于是它也回一个 SYN=1,并带上自己的初始序列号 seq=y。

同时,把客户端的 seq+1 作为确认号 ack=x+1,
意思是:“我已经收到了你的SYN,老铁。”

这次回应中 SYN=1,ACK=1
服务端进入 SYN_RCVD 状态,
心想:“兄弟挺有诚意,我也回应一波。”


第三次握手:客户端 → 服务端

客户端收到服务端的 SYN 后,再回一个 ACK=1 的确认报文,
将服务端的 seq+1 作为确认号 ack=y+1,
表示:“我也收到了你的SYN,咱们可以正式开始了!”

这时候客户端进入 ESTABLISHED 状态,
而服务端在收到这个 ACK 后,也进入 ESTABLISHED 状态,

连接就正式建立了!

用大白话来讲就是:

第一次:客户端喊一声:“喂,你能听到吗?”
第二次:服务端回应:“我能听到,你能听到我吗?”
第三次:客户端再确认:“我也能听到,咱们可以开始了。”

这三轮对话一过,连接就算正式建立了

接下来就可以放心传数据了,不怕断、不怕乱、不怕丢!

Part5要三次握手?两次不行吗?

🤔 先问个问题:为啥非得三次握手?

三次握手的目的很简单:
确保双方的发送和接收能力都正常,并且同步初始序列号(ISN),为后续可靠传输打基础。

🚀 那两次握手行不行呢?

想象一下这个场景:

  • 客户端发出连接请求
  • ,但因网络原因,第一个请求报文丢了。
  • 客户端再发一次请求,这次服务端收到了并且回应了确认。
  • 数据传输完毕后,连接释放了。
  • 然而,之前丢失的那个请求报文在某个角落里“冬眠”了一段时间,突然醒了,又飘到了服务端。
  • 服务端以为这是个新的连接请求,于是又开始建立连接,结果发现客户端不理它……

这就导致了一个问题:服务端一直在等待一个永远不会来的数据包,浪费了资源!

因此,两次握手不够保险,必须通过第三次握手来确保双方的状态都正常。


Part6什么是半连接队列?

🔍 半连接队列是啥?

当服务端第一次收到客户端的 SYN 请求时,会进入 SYN_RCVD 状态。
此时,连接还没完全建立,服务端会把这种状态下的请求放在一个队列中,这就是半连接队列

还有个全连接队列,用于存放已经完成三次握手的连接。
如果队列满了,可能会导致丢包现象。

⏳ 关于重传次数

服务端发送完 SYN-ACK 后,如果没收到客户端的确认,会进行重传。
每次重传的时间间隔通常会指数增长(如 1s, 2s, 4s, 8s……),直到超过系统规定的最大重传次数,才会从半连接队列中删除该连接。

Part7ISN 是固定的吗?

🔢 ISN 是动态变化的

ISN 是一个随时间变化的32位计数器,每4毫秒加1。

这样设计是为了防止旧的分组在网络中滞留太久,导致错误解释。

固定ISN的风险

如果ISN是固定的,攻击者很容易猜出后续的确认号,从而发起攻击。

所以,ISN是动态生成的,以增加安全性。

Part8三次握手可以携带数据吗?

第三次握手是可以携带数据的,但第一次和第二次握手不可以。

为什么呢?

假如第一次握手可以携带数据,恶意攻击者就可以在 SYN 报文中放入大量数据,疯狂重复发送 SYN 报文,导致服务器花费大量时间和内存处理这些垃圾报文。

  • 第一次握手不能带数据
  • :防止恶意攻击。
  • 第三次握手可以带数据
  • :因为此时客户端已处于 ESTABLISHED 状态,知道服务端的接收和发送能力正常。

Part9什么是 SYN 攻击?

⚔️ SYN 攻击是什么?

SYN 攻击是一种典型的 DoS/DDoS 攻击手段。
攻击者会在短时间内伪造大量不存在的 IP 地址,向服务器发送大量的 SYN 请求。
服务端回复确认包后,等待客户端确认,但由于源地址是伪造的,这些确认包永远不会到达,导致服务端资源被占用。

🚨 如何检测和防御?

检测:

你可以使用以下命令来检测是否遭受 SYN 攻击:

netstat -n -p TCP | grep SYN_RECV

如果你看到大量来自随机IP地址的半连接状态,基本上可以断定你正在遭受 SYN 攻击。

防御方法:

  1. 缩短超时时间(SYN Timeout)
  2. :减少等待确认的时间。
  3. 增加最大半连接数
  4. :提高系统承受能力。
  5. 过滤网关防护
  6. :设置防火墙规则,限制异常流量。
  7. SYN cookies技术
  8. :通过算法生成SYN-ACK响应,减少对内存资源的依赖。

Part10TCP 四次挥手

如果说三次握手是“确认关系”,那四次挥手就是“和平分手”。

TCP 是个很讲究感情的协议,它不会像 UDP 那样“说完就走”,而是要一步步说清楚:“我要走了”、“我知道你要走了”、“我也要走了”、“收到,拜了个拜。”

整个过程就像情侣分手一样,不是一句话就能搞定的。

而且,分手之后还要等一会儿才能彻底断开连接,这叫“2MSL 等待状态”。

💬 初始状态:

  • 客户端和服务端都处于 ESTABLISHED 状态;
  • 假设客户端想主动关闭连接,开始四次挥手。

第一次挥手:客户端 → 服务端

客户端发了一个 FIN 报文,表示:“我不再传数据了。”
此时客户端进入 FIN_WAIT1 状态。

FIN = 1,seq = u
不再发送新数据,但仍可以接收数据。

第二次挥手:服务端 → 客户端

服务端收到 FIN 后,马上回一个 ACK 报文,表示:“我知道你要走了。”

并把客户端的序号+1作为确认号返回。

此时服务端进入 CLOSE_WAIT 状态,客户端进入 FIN_WAIT2 状态。

ACK = 1,ack = u+1,seq = v

这时候只是单向关闭,服务端还能继续发数据给客户端。

第三次挥手:服务端 → 客户端

服务端处理完自己的数据后,也准备关闭连接,于是也发一个 FIN 报文,表示:“我也要走了。”
此时服务端进入 LAST_ACK 状态。

FIN = 1,ACK = 1,seq = w,ack = u+1

第四次挥手:客户端 → 服务端

客户端收到服务端的 FIN 后,回一个 ACK 报文,表示:“收到啦,咱们正式拜拜。”

然后客户端进入 TIME_WAIT 状态,等待一段时间后才真正关闭连接。

服务端收到 ACK 后直接进入 CLOSED 状态。

ACK = 1,ack = w+1,seq = u+1

客户端必须等 2MSL 时间才能彻底关闭连接。

Part11挥手为什么需要四次?两次不行吗?

因为 TCP 是全双工通信,双方都能发数据

所以不能像三次握手那样“合并发送”。

举个栗子🌰:

当服务端收到客户端的 FIN 时

它可能还有数据没发完,不能立马断开连接

只能先回一个 ACK 表示“我收到了”

等自己数据发完了,再发一个 FIN 表示“我也好了”。

所以中间这个缓冲期,就导致了四次挥手。

如果强行合并成两次,那就可能出现一边关了另一边还在傻等的情况

这就叫“半死不活”的连接,系统资源浪费严重!

Part12TIME_WAIT状态为什么要等2MSL?

这个“2MSL”听起来很神秘,其实它就是:

Maximum Segment Lifetime 的两倍时间,也就是报文段在网络中能存活的最长时间。

比如 MSL 是 60 秒,那 2MSL 就是 120 秒。

为啥要等这么久?

两个关键原因:


✅ 1. 确保最后一个 ACK 能到达服务端

万一客户端发的这个 ACK 丢了怎么办?

服务端会因为没收到 ACK 而重新发 FIN

这时候客户端还没彻底关闭,就能重新响应 ACK

避免服务端一直卡在 LAST_ACK 状态

如果不等 2MSL,客户端一发完就跑路

服务端收不到确认,就会无限重传,直到超时


✅ 2. 防止旧连接的报文干扰新连接

假设客户端不等 2MSL,直接关闭连接

而某个老报文在路上绕了一圈又回来了

这时候刚好有个新连接使用了同样的五元组(IP + 端口)

那这个老报文就可能被当成新连接的数据处理,造成混乱。

等 2MSL 的目的,就是让网络上所有属于旧连接的报文都“寿终正寝”

这样新连接就不会误判这些“幽灵报文”

为帮助理解 TCP 连接建立(三次握手)与终止(四次挥手)的状态变化,特提供一张典型的状态变迁图(见下图)。其中,粗实线箭头指示客户端的标准状态变迁,粗虚线箭头指示服务器端的标准状态变迁。

Part13TIME_WAIT 和 CLOSE_WAIT

TCP 中的两个“尴尬状态”:TIME_WAIT 和 CLOSE_WAIT

这两个状态可以说是 TCP 通信中的“分手后遗症”

一个是因为分手太慢被卡住(TIME_WAIT)

另一个是因为对方迟迟不分手而僵持(CLOSE_WAIT)

下面我来给你讲清楚它们到底啥意思、为啥会出问题、怎么解决!

🕒 TIME_WAIT 状态 —— 主动分手者的等待期

“我说拜拜了,你确定收到没?再等一会儿我才彻底走。”

✅ 它是谁触发的?

主动关闭连接的一方触发。比如客户端调用了 close() 或 shutdown(),它就进入了“分手流程”。

🤔 它为什么存在?

主要是为了两个目的:

  1. 确保最后一个 ACK 能到达服务端
    如果这个 ACK 丢了,服务端就会重传 FIN,客户端还能响应。
  2. 防止旧连接的报文干扰新连接
    报文在网络里最多活 MSL 时间,等 2MSL 就能保证这些“幽灵包”都消失了。

MSL 是 Maximum Segment Lifetime,即报文段的最大生存时间,一般是 30s~60s。

所以 TIME_WAIT 的持续时间就是 2 × MSL,大约 60s 到 120s 不等。

⚠️ 它的问题是什么?

  • 占用本地端口号资源;
  • 大量 TIME_WAIT 可能导致端口耗尽;
  • 对高并发短连接服务器影响较大(如 Web 服务器);

🔧 怎么解决大量 TIME_WAIT?

可以用 setsockopt 设置套接字选项,允许地址和端口复用:

int opt = 1;
setsockopt(listensock, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt));

这样就能避免因为 TIME_WAIT 导致的端口占用问题啦!

🐌 CLOSE_WAIT 状态 —— 被动分手者的拖延症

“你说要走了,我也收到了,但我还没关呢,你在等我……我在等你……”

✅ 它是谁触发的?

被动关闭连接的一方触发。比如客户端先发 FIN,服务端回 ACK 后进入 CLOSE_WAIT。

🤔 它为什么存在?

表示服务端已经知道客户端要关闭连接了,但它还在等应用程序去处理剩下的数据,并手动关闭 socket。

⚠️ 它的问题是什么?

如果程序忘了调用 close() 去关闭 socket,
那这个连接就会一直卡在 CLOSE_WAIT,
最终导致连接数爆炸、资源泄漏、服务崩溃……

💡 常见原因:

  • 忘记关闭 socket;
  • 没有对 read/write 返回值做正确判断;
  • 多线程/异步编程中未及时释放资源;

🔧 怎么排查和解决?

可以使用如下命令查看当前系统的 CLOSE_WAIT 数量:

netstat -antp | grep CLOSE_WAIT

一旦发现很多 CLOSE_WAIT,就要检查代码有没有漏掉 close() 或者异常退出没有清理资源。

总结

TCP 的连接和断开,看似只是几个来回的数据包,但背后却藏着网络通信中最核心的设计理念:可靠传输、资源管理、防止混乱

从“三次握手”到“四次挥手”,再到“TIME_WAIT”和“CLOSE_WAIT”,这些状态不是为了难为我们,而是为了让整个互联网更稳定、更安全地运行。

作为开发者,理解它们不仅有助于应对面试官的灵魂拷问,更重要的是能在实际工作中排查问题、优化系统、写出更健壮的网络程序!


📌 如果你是初学者:建议动手抓个包看看三次握手和四次挥手的实际过程(Wireshark 走起);

📌 如果你是进阶者:不妨研究下 TCP 的保活机制、端口复用、以及高并发下的连接管理;

📌 如果你是面试党:记住一句话——能画图、能举例、能说出坑在哪,才是真掌握!

📢面试中TCP高频面试题部分列举

  1. 请画出三次握手和四次挥手的示意图
  2. 为什么连接的时候是三次握手?
  3. 什么是半连接队列?
  4. ISN(Initial Sequence Number)是固定的吗?
  5. 三次握手过程中可以携带数据吗?
  6. 如果第三次握手丢失了,客户端服务端会如何处理?
  7. SYN攻击是什么?
  8. 挥手为什么需要四次?
  9. 四次挥手释放连接时,等待2MSL的意义?
  10. TCP和UDP的主要区别是什么?
  11. TCP是如何保证可靠传输的?(关键机制)
  12. TCP的滑动窗口机制是什么?它解决了什么问题?(流量控制)
  13. TCP的拥塞控制算法主要有哪些阶段?描述其基本原理。(慢启动、拥塞避免、快重传、快恢复)
  14. 什么是TCP的粘包和拆包问题?为什么会出现?如何解决?
  15. TCP的Keepalive机制是什么?它的作用是什么?有什么缺点?与应用层心跳有何区别?
  16. 描述TCP的状态机(重点描述连接建立和关闭过程中的状态变迁)。
  17. 为什么要有TIME_WAIT状态?过多的TIME_WAIT状态有什么影响?如何优化?
  18. TCP头部结构包含哪些关键字段?(至少说出6-8个并说明作用)
  19. TCP的最大报文段长度(MSS)是什么?它是如何确定的?与MTU有什么关系?
  20. 什么是Nagle算法?它的目的是什么?在什么场景下可能需要关闭它?
  21. 什么是延迟确认(Delayed ACK)?它的目的是什么?
  22. TCP真的100%可靠吗?(可靠性的边界)
  23. MTU和MSS有什么区别?
  24. TCP如何检测和处理丢包?(超时重传 vs 快速重传)
  25. TCP连接建立后,如果通信双方一直不发送数据,连接会一直保持吗?(涉及Keepalive和中间设备超时)

往期推荐

【大厂标准】Linux C/C++ 后端进阶学习路线

C/C++ 高频八股文面试题1000题(一)

C/C++ 高频八股文面试题1000题(二)

点击下方关注【Linux教程】,获取编程学习路线、项目教程、简历模板、大厂面试题、大厂面经、编程交流圈子等等。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/pingmian/88473.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/88473.shtml
英文地址,请注明出处:http://en.pswp.cn/pingmian/88473.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

RuoYi+Uniapp(uni-ui)开发商城系统

如果你正在考虑用 RuoYi 和 UniApp(uni-ui)搭建一套商城系统,那这套组合确实值得好好研究。它整合了 RuoYi 的快速开发能力和 UniApp 的跨平台特性,在高效开发的同时还能兼顾多端适配的需求。下面从技术架构、功能模块、开发实践到…

面试150 二叉树的最大高度

思路 考虑从递归出发,联想递归三部曲:返回什么、传入的参数是什么、遍历的方式是什么。此题现在需要我们整个树,并且需要从根节点出发,因此我们选择先序遍历即可。另一张办法,则是选择通过队列实现层次遍历&#xff0c…

从零实现一个GPT 【React + Express】--- 【2】实现对话流和停止生成

摘要 这是本系列文章的第二篇,开始之前我们先回顾一下上一篇文章的内容: 从零实现一个GPT 【React Express】— 【1】初始化前后端项目,实现模型接入SSE 在这一篇中,我们主要创建了前端工程和后端工程,这里贴一下我…

SEQUENCE在RAC多实例开启CACHE的NEXTVAL数值乱序问题

问题说明 在多实例环境中可能会出现从Sequence所取出来的nextval是乱序的,比如第二次比第一次所取的数要小但这并不是我们所希望的。当程序逻辑Base on sequence.nextval数值所谓填充字段的大小来排序时,就会产生问题。 实际上就是由于多实例这一特性造成…

后台管理系统-权限管理

在后台管理系统当中,权限管理占着非常重要的位置,权限管理,顾名思义,就是用来管理用户登录后台的权限。 在权限管理中有三个重要的名词:账号,角色,权限 账号:通过账号进入平台&…

MySQL表的约束(5)

文章目录前言一、空属性二、默认值三、列描述四、zerofill五、主键六、自增长七、唯一键八、外键总结前言 真正约束字段的是数据类型,但是数据类型约束很单一,需要有一些额外的约束,更好的保证数据的合法性,从业务逻辑角度保证数据…

MyBatis:SQL与Java的智能桥梁

MyBatis:SQL 与 Java 的「智能翻译官」 —— 用 极简的方式 连接数据库和 Java 对象,告别 JDBC 的繁琐操作!核心定位:半自动化 ORM 框架对比项JDBC 原生操作MyBatis 解决方案SQL 编写拼字符串(易出错、难维护&#xff…

自动驾驶控制系统

目录 控制系统概述 无人车控制架构设计 自动驾驶控制核心技术 车辆纵向控制 车辆横向控制 自动驾驶控制方法 自动驾驶控制技术方案 人机交互系统 控制系统概述 控制技术是智能驾驶的关键,旨在环境感知技术的基础之上,根据决策规划出目标轨迹,通过纵向和横向控制系统…

网络安全基石:从弱口令治理到动态防御体系的构建

引言:数字时代的防御困局 在5G与物联网技术全面落地的数字新基建时代,企业网络资产规模呈现指数级增长。Verizon《2023年数据泄露调查报告》显示,61%的安全事件直接源于凭证失窃,而其中81%的攻击成功案例可溯源至初始口令强度的不…

Error: fatal: detected dubious ownership in repository at

这个错误是 Git 在新版中引入的一种 安全检查机制,目的是防止不同用户访问同一个 Git 仓库目录,避免潜在的权限或安全问题。你的情况是:仓库目录是属于另一个用户。当前以管理员用户 OVERSPREAD/Administrator 运行 Git。Git 为了安全起见&am…

嵌入式 数据结构学习 (六) 树、哈希表与内核链表

一、树(Tree)结构详解1. 树的基本概念树的核心特性非线性结构:数据元素之间存在一对多的层次关系递归定义:树的子树仍然是树专业术语:度(Degree):结点拥有的子树数叶子结点:度为0的结点层次(Level):根为第1…

封装WebSocket

一个基于原生 WebSocket 的封装库,实现了自动重连、心跳检测等功能,用于在前端应用中稳定地与后端 WebSocket 服务通信。下面从设计思路、关键功能等方面进行详细分析:设计思路 这个封装库采用单例模式设计,全局维护一个 WebSocke…

SLICEGPT: COMPRESS LARGE LANGUAGE MODELSBY DELETING ROWS AND COLUMNS

发表:ICLR24 机构:ETH Zurich 连接:https://arxiv.org/pdf/2401.15024 ABSTRACT 大型语言模型(Large Language Models, LLMs)已成为自然语言处理的基石,但其使用伴随着在计算和内存资源方面的高昂代价。…

Python 【技术面试题和HR面试题】➕ 循环结构、控制语句及综合应用问答

1.技术面试题 (1)详细描述单调栈的工作原理和应用场景 答: 原理 维护栈内元素单调递增 / 递减,新元素入栈前,弹出破坏单调性的栈顶,保持单调。 应用场景 排队比身高,搭积木找最大的空地 &#x…

100G系列光模块产品与应用场景介绍

在当今数字化时代,网络流量呈爆炸式增长,对数据传输速度和带宽的要求也越来越高。100G 光模块作为高速数据传输的关键组件,因其卓越的高速传输能力,已成为数据中心、云计算、企业网络以及 5G 通信网络等领域的重要组成部分。接下来…

运筹说 第140期 | 从直觉到算法:这些奠基人如何塑造了启发式方法的科学根基?

运筹说建构知识体系,解析学习要点运 筹 优 化 领 域 教 学 媒 体视频课程已上线!!!欢迎大家关注同名抖音和哔哩哔哩账号!在人工智能与优化科学的浩瀚星空中,启发式算法如同一把钥匙,为人类打开了处…

Flutter编译安卓应用时遇到的compileDebugJavaWithJavac和compileDebugKotlin版本不匹配的问题

记一次flutter应用,编译安卓时,报的一个compileDebugJavaWithJavac和compileDebugKotlin版本本匹配的问题。 最终定位的原因是项目一来了audioplayers组件。 audioplayers组件有依赖了audioplayers_android, 它使用1.8编译的。 版本过低。后来…

linux-权限管理

linux-权限管理一、权限的基本类型二、权限的表示方式1. 字符形式(rwx)2. 数字形式三、权限管理常用命令1. chmod2. chown3. chgrp四、隐藏权限1. lsattr2. chattr五、权限掩码六、特别权限位1. suid2. sgid3. Sticky Bit七、权限委托1. 授权用户2. 授权…

从FCOS3D到PGD:看深度估计如何快速搭建你的3D检测项目

【导读】 还记得那个曾经在单目3D目标检测领域掀起热潮的 FCOS3D 吗?在后续更新中他们又推出了全新升级版——PGD(Probabilistic and Geometric Depth)最有意思的是,这次他们彻底换了路线:从原先的“直接回归深度”&a…

Apache Cloudberry 向量化实践(三)重塑表达式构建路径:Gandiva 优化实战

在向量化执行系统中,表达式构建是不可或缺的基础环节。无论是 SQL 中的投影、筛选,还是分区、聚合、排序,最终都需转化为底层执行引擎能识别和执行的表达式树。而在 Apache Cloudberry 向量化执行框架中,这一过程由 Gandiva 表达式…