【前端面试小册】网络-第6节:3次握手、4次挥手
一、概述
在网络协议第1节,我们已经了解到了访问网站会先通过 DNS 解析获取到目标网址的 IP,而得到 IP 后我们要进行数据传输,必须先进行三次握手才会建立起连接,才能开始通信,而通信结束之后会进行4次挥手。
本节我们将详细了解3次握手和4次挥手、应对面试官的回答。
类比理解:
- 三次握手:就像打电话前的确认过程
- 四次挥手:就像打完电话后的告别过程
二、三次握手理解
2.1 生活类比
A 想要约 B 吃饭,于是发短信:
- A:我要约你吃饭
- B:好的,时间地点是 xxx
- A:我知道时间地点了
2.2 技术术语
- A:
seq=x,syn=1,SYN_SENT状态 - B:
Ack=seq+1=x+1,seq=y,SYN_RCVD收到 - A:
seq=x+1,ack=y+1,ESTABLISHED状态 - B:收到 ack 后,
ESTABLISHED状态
2.3 流程图
graph TD
A[客户端 CLOSED] --> B[发送 SYN seq=x]
B --> C[客户端 SYN_SENT]
C --> D[服务器接收]
D --> E[发送 SYN+ACK seq=y ack=x+1]
E --> F[服务器 SYN_RCVD]
F --> G[客户端接收]
G --> H[发送 ACK seq=x+1 ack=y+1]
H --> I[客户端 ESTABLISHED]
I --> J[服务器接收]
J --> K[服务器 ESTABLISHED]
K --> L[连接建立成功]
三、面试官:为什么是三次握手?
3.1 核心目的
主要目的防止 server 端一直等待,浪费资源。
结合上面例子:第三步的目的就是告诉 B:我已经知道时间地点了,B 收到信息后,确定 A 了解时间地点,晚上能正确赴约后,B 就可以该干什么干什么去了。
3.2 从客户端和服务端视角来看
第一次握手
客户端发送网络包,服务端收到了。
服务端得出结论:
- 客户端的发送能力正常 ✅
- 服务端的接收能力正常 ✅
第二次握手
服务端发包,客户端收到了。
客户端得出结论:
- 服务端的接收能力正常 ✅
- 服务端的发送能力正常 ✅
- 客户端的接收能力正常 ✅
- 客户端的发送能力正常 ✅
分析:
- 从客户端的视角来看,我接到了服务端发送过来的响应数据包
- 说明服务端接收到了我在第一次握手时发送的网络包,并且成功发送了响应数据包
- 这说明,服务端的接收、发送能力正常
- 而另一方面,我收到了服务端的响应数据包,说明我第一次发送的网络包成功到达服务端
- 这样,我自己的发送和接收能力也是正常的
第三次握手
客户端发包,服务端收到了。
服务端得出结论:
- 客户端的接收能力正常 ✅
- 客户端的发送能力正常 ✅
- 服务端的发送能力正常 ✅
- 服务端的接收能力正常 ✅
分析:
- 第一、二次握手后,服务端其实并不知道客户端的接收能力以及自己的发送能力是否正常
- 而在第三次握手时,服务端收到了客户端对第二次握手作的回应
- 从服务端的角度,我在第二次握手时的响应数据发送出去了,客户端接收到了
- 所以,我的发送能力是正常的
- 而客户端的接收能力也是正常的
3.3 为什么不是两次或四次?
两次握手的问题
如果只有两次握手:
- 客户端发送连接请求
- 服务端响应并建立连接
- 问题:如果客户端的请求在网络中滞留,后来又到达服务器,服务器会误认为是新连接,白白浪费资源
四次握手不必要
三次握手已经足够确认双方的发送和接收能力,四次握手没有必要。
四、三次握手详细流程
4.1 开始
双方都处于关闭状态(CLOSED),显示服务器先监听某一个端口,处于监听状态(LISTEN)。
4.2 第一次握手
建立连接时,客户端主动发送报文段(SYN=1,seq=x)到服务器,并进入 SYN_SENT 状态,等待服务器确认。
关键字段:
SYN=1:同步序列编号,表示建立连接seq=x:客户端选择的初始序列号
4.3 第二次握手
服务器收到 SYN 报文段,必须确认客户的报文段。此时会响应一个报文段,这个报文段的 SYN=1,控制位 ACK=1,ack号=x+1(对应第一次握手中的 seq 数+1),seq=y,然后服务器进入 SYN_RECV 状态。
关键字段:
SYN=1:同步序列编号ACK=1:确认号有效ack=x+1:确认收到客户端的 seqseq=y:服务器选择的初始序列号
4.4 第三次握手
客户端收到服务器的 SYN+ACK 包后,需要响应服务器的应答报文段,这个报文段的控制位 ACK=1,ack号=y+1(对应第二次握手中的 seq 数+1),seq=x+1(对应第二次握手中的 ack 号),然后客户端进入建立连接(ESTABLISHED)的状态,服务端接受到这个报文段后也进入建立连接(ESTABLISHED)的状态。
关键字段:
ACK=1:确认号有效ack=y+1:确认收到服务器的 seqseq=x+1:客户端的序列号
4.5 客户端如何判断通信是否过期
通过 RST(Reset Flag),如果该位为1,表示 TCP 连接中出现异常必须强制断开连接。
判断逻辑:
- 如果当前连接是历史连接,即 SEQ 过期或者超时,那么发送方就会直接发送 RST 控制消息中止这一次连接
- 如果当前连接不是历史连接,那么发送方就会发送 ACK 控制消息,通信双方就会成功建立连接
所以,客户端第三次根据 seq 判断是否过期来决定是否断开此次通信连接。
五、三次握手常见问题
5.1 如果已经建立了连接,但是客户端突然出现故障了怎么办?
TCP 还设有一个保活计时器。
工作原理:
- 显然,客户端如果出现故障,服务器不能一直等下去,白白浪费资源
- 服务器每收到一次客户端的请求后都会重新复位这个计时器,时间通常是设置为 2 小时
- 若两小时还没有收到客户端的任何数据,服务器就会发送一个探测报文段
- 以后每隔 75 秒钟发送一次
- 若一连发送 10 个探测报文仍然没反应,服务器就认为客户端出了故障,接着就关闭连接
5.2 什么是半连接队列
服务器第一次收到客户端的 SYN 之后,就会处于 SYN_RCVD 状态,此时双方还没有完全建立其连接,服务器会把此种状态下请求连接放在一个队列里,我们把这种队列称之为半连接队列。
全连接队列:已经完成三次握手,建立连接的队列。
5.3 三次握手过程中可以携带数据吗
其实第三次握手的时候,是可以携带数据的。但是,第一次、第二次握手不可以携带数据。
为什么只有第三次可以携带数据?
原因:防止攻击。
详细分析:
- 如果第一次握手可以携带数据的话,如果有人要恶意攻击服务器
- 那他每次都在第一次握手中的 SYN 报文中放入大量的数据
- 攻击者不关心服务器的接收、发送能力是否正常,然后疯狂着重复发 SYN 报文的话
- 占用服务器内存,没时间处理其他连接
而对于第三次的话,此时客户端已经处于 ESTABLISHED 状态,对于客户端来说,他已经建立起连接了,并且也已经知道服务器的接收、发送能力是正常的了,所以能携带数据了。
5.4 SYN 攻击是什么
SYN 攻击是一种典型的 DoS/DDoS 攻击。
SYN 攻击情况
- 客户端在短时间内伪造大量不存在的 IP 地址,并向 Server 不断地发送 SYN 包
- 服务端向客户端回复确认包,并等待客户端确认
- 由于源地址不存在,因此 Server 需要不断重发直至超时
- 这些伪造的 SYN 包将长时间占用未连接队列,导致正常的 SYN 请求因为队列满而被丢弃,从而引起网络拥塞甚至系统瘫痪
SYN 攻击检测
netstat -n -p TCP | grep SYN_RECV
在服务器上看到大量的半连接状态时,特别是源 IP 地址是随机的,基本上可以断定这是一次 SYN 攻击。
防御措施
- SYN Cookie:不立即分配资源,而是计算一个 cookie
- 增加半连接队列大小
- 减少超时时间
- 使用防火墙:过滤异常 IP
六、四次挥手
6.1 四次挥手流程
当客户端和服务器通过三次握手建立了 TCP 连接以后,当数据传送完毕后需要要断开 TCP 连接。而断开连接需要 4 次。
名称解释:
- seq:Sequence Number,请求序号
- ACK:Acknowledgment 确认报文
- FIN:终止报文
第一次挥手
主机1(可以是客户端,也可以是服务器端,这里当做客户端),发出连接释放报文段(FIN=1,序号 seq=u),并停止再发送数据,主动关闭 TCP 连接,等待主机2(服务端)的确认,此时,主机1进 FIN_WAIT1(终止等待1)状态。
含义:这表示主机1没有数据要发送给主机2了。
第二次挥手
服务端收到了客户端发送的 FIN 报文段,会向客户端回一个 ACK 报文段并且把客户端的序列号值 +1 作为 ACK 报文的序列号值,所以服务端发给客户端的确认报文(ACK=1,确认号 ack=u+1,序号 seq=v)表明已经收到客户端的报文了,并"同意"你的关闭请求,此时服务端处于 CLOSE_WAIT 状态。
注意:但是这个时候服务端有可能还有数据在传输,所以并没有发送 LAST_ACK 标识。
第三次挥手
如果服务端没有要向客户端发出的数据后,会向客户端发出连接释放报文段(FIN=1,ACK=1,序号 seq=w,确认号 ack=u+1),同时服务端进入 LAST_ACK(最后确认)状态,等待客户端的确认。
第四次挥手
客户端收到服务端 FIN 报文段后,向服务端发送确认报文段(ACK=1,seq=u+1,ack=w+1),客户端进入 TIME_WAIT(时间等待)状态。此时 TCP 未释放掉,需要经过时间等待计时器设置的时间 2MSL 后没有收到回复,则证明服务端已正常关闭,客户端才进入 CLOSED 状态。
到此 TCP 的四次分手就完成了。
6.2 四次挥手流程图
graph TD
A[客户端 ESTABLISHED] --> B[发送 FIN seq=u]
B --> C[客户端 FIN_WAIT1]
C --> D[服务器接收]
D --> E[发送 ACK ack=u+1 seq=v]
E --> F[服务器 CLOSE_WAIT]
F --> G[客户端接收]
G --> H[客户端 FIN_WAIT2]
H --> I[服务器发送剩余数据]
I --> J[发送 FIN+ACK seq=w ack=u+1]
J --> K[服务器 LAST_ACK]
K --> L[客户端接收]
L --> M[发送 ACK ack=w+1]
M --> N[客户端 TIME_WAIT]
N --> O[等待 2MSL]
O --> P[客户端 CLOSED]
P --> Q[服务器接收 ACK]
Q --> R[服务器 CLOSED]
七、为什么需要4次而不是3次
7.1 第一次意义
客户端通知服务端,关闭连接,这个肯定需要不能少。
7.2 第二次、第三次意义
问题:这里的关键是第2次发送 ACK(确认报文)和第三次发送 FIN(终止报文)为什么不合并一次发送,而需要分开发送?
第二次理由:
- 服务端收到客户端关闭连接的时候,只是说明客户端不再发送数据了
- 但是服务端此时可能还有没发送完的数据
- 因为发送数据可能会很久,于是服务端先执行第二次挥手
- 回复(ACK)告诉客户端我知道你不发送数据了
第三次理由:
- 等服务端没有数据要发送了,就会执行第三次挥手发送 FIN 报文
- 告诉客户端我没有数据要发送了
- 这个时候客户端就可以执行第四次挥手
- 此时服务端并未关闭 TCP 连接,而是处于
LAST_ACK(最后确认状态)
7.3 第四次意义
接收到服务端的 FIN 后,客户端需要通知服务端我收到你的终止报文,你可以从 LAST_ACK(等待确认)变成关闭连接了。
八、2MSL 是什么?有什么作用?
8.1 2MSL 定义
MSL:Maximum Segment Lifetime,最大生存时间。
2MSL:2倍的 MSL 时间。
8.2 作用
客户端在第四次挥手后,传送 FIN+ACK 报文后不会立即关闭连接,而是会等待 2MSL,一切正常后才会关闭连接处于 CLOSED 状态。
8.3 等待 2MSL 原因
问题场景:
- 客户端第四次挥手传给服务端的 ACK 确认报文有可能丢失
- 这样会导致处于
LAST-ACK状态的服务端收不到客户端发送的 FIN+ACK 报文段的确认 - 一直处于
LAST_ACK而不关闭连接
解决方案:
- 如果服务端没有收到 FIN+ACK 会超时重新传 FIN 给客户端
- 接着客户端会重新再传 FIN+ACK 给服务端,并重新启动时间等待计时器
- 这样客户端和服务端都能正常关闭
如果不等待 2MSL:
- 客户端在发送完 ACK 之后直接释放关闭
- 一旦这个 ACK 丢失的话,服务器就无法正常的进入关闭连接状态
8.4 MSL 时间
- Linux 中 MSL 通常为 30 秒或 2 分钟
- 所以 2MSL 为 1-4 分钟
九、面试要点总结
核心知识点
- 三次握手:确认双方发送和接收能力
- 四次挥手:安全关闭连接
- 为什么三次:两次不够,四次不必要
- 为什么四次:服务端可能还有数据未发送完
- 2MSL:确保连接可靠关闭
常见面试题
Q1: 为什么需要三次握手?
答:
- 确认双方的发送和接收能力都正常
- 防止失效的连接请求到达服务器,造成资源浪费
- 两次不够(无法确认客户端接收能力),四次不必要
Q2: 为什么需要四次挥手?
答:
- 客户端可以不发数据,但服务端可能还有数据要发送
- 第二次和第三次不能合并,因为服务端可能还有数据未发送完
- 需要等待服务端发送完所有数据
Q3: 什么是 2MSL?为什么要等待 2MSL?
答:
- MSL 是最大生存时间,2MSL 是两倍的 MSL
- 等待 2MSL 是为了确保最后的 ACK 能够到达服务器
- 如果 ACK 丢失,服务器会重传 FIN,客户端需要能够响应
实战建议
- ✅ 理解三次握手和四次挥手的每一步意义
- ✅ 掌握各种状态(SYN_SENT、ESTABLISHED、FIN_WAIT 等)
- ✅ 了解常见问题(SYN 攻击、TIME_WAIT 过多等)
- ✅ 能够画出流程图说明整个过程