面试官:你再问TCP三次握手,我就要报警了!

发布于:2025-07-12 ⋅ 阅读:(18) ⋅ 点赞:(0)

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教程】,获取编程学习路线、项目教程、简历模板、大厂面试题、大厂面经、编程交流圈子等等。


网站公告

今日签到

点亮在社区的每一天
去签到