关于三次握手与四次挥手面试官想考我们什么?——不看后悔系列
Category(分类): NET Protocol Status: 优
在面试中,三次握手和四次挥手可以说是被问得最频繁的知识点之一。大家可能已经看过很多相关的文章,本文重点从面试角度梳理三次握手和四次挥手中比较重要、也比较容易被问到的内容。
下面的流程以最常见的“客户端主动建立连接、客户端主动关闭连接”为例。TCP 还支持同时打开、同时关闭、RST 异常关闭等情况,文中会在相应位置补充说明。
三次握手
由于三次握手在面试中出现得非常频繁,下面从面试的角度讲解三次握手。
当面试官问“为什么需要三次握手”“三次握手有什么作用”或者“讲讲三次握手”时,可以先回答基本过程:
- 第一次握手:客户端向服务器发送一个
SYN报文,并携带客户端的初始序列号ISN(c)。 - 第二次握手:服务器收到
SYN后,回复一个SYN+ACK报文,同时携带服务器的初始序列号ISN(s),并确认客户端的 SYN。 - 第三次握手:客户端收到
SYN+ACK后,回复一个ACK报文,确认服务器的 SYN。 - 服务器收到最后的
ACK后,双方进入ESTABLISHED状态,连接建立完成。
三次握手的作用不仅是确认双方在 TCP 层面的收发路径基本正常,还包括:
- 同步客户端和服务器各自的初始序列号;
- 确认双方都收到了对方的连接建立报文;
- 降低历史重复 SYN 报文导致错误建立连接的可能性。
三次握手的详细过程
刚开始客户端处于 CLOSED 状态,服务端处于 LISTEN 状态。设客户端的初始序列号为 x,服务器的初始序列号为 y:
- 第一次握手:客户端向服务端发送一个 SYN 报文:
<SEQ=x><SYN>
客户端发送完成后进入SYN-SENT状态,等待服务端的响应。 - 第二次握手:服务器收到客户端的 SYN 后,发送一个同时带有 SYN 和 ACK 标志的报文:
<SEQ=y><ACK=x+1><SYN,ACK>ACK=x+1表示服务器已经收到客户端序列号为x的 SYN。由于 SYN 本身会占用一个序列号,服务器确认时使用x+1。服务器发送 SYN+ACK 后进入SYN-RECEIVED状态,等待客户端最后的 ACK。 - 第三次握手:客户端收到服务端的 SYN+ACK 后,发送 ACK 报文:
<SEQ=x+1><ACK=y+1><ACK>ACK=y+1表示客户端已经收到服务端的 SYN。客户端处理完 SYN+ACK 并发送最后的 ACK 后进入ESTABLISHED状态。 - 服务器收到最后的 ACK 后,也进入
ESTABLISHED状态。此时,双方已经建立 TCP 连接,可以开始传输数据。

如果 SYN 或 SYN+ACK 中携带了数据,确认号还需要加上对应的数据长度。上面的
x+1和y+1是经典的、不携带应用数据的三次握手示例。
为什么需要三次握手,而不是两次?
第一次握手时,客户端发送网络包,服务端收到了。此时服务端可以得出结论:客户端的发送能力、服务端的接收能力基本正常。
第二次握手时,服务端发送 SYN+ACK,客户端收到了。此时客户端可以得出结论:服务端的发送能力、客户端的接收能力基本正常,同时客户端也知道服务端已经收到了自己的 SYN。
但是,仅仅到第二次握手,服务器还不能确认客户端是否收到了自己的 SYN+ACK。第三次握手中,客户端发送 ACK,服务器收到后,才能确认客户端已经收到了服务端的 SYN+ACK。
因此,三次握手的关键不是简单地“必须发送三个包”,而是让双方都确认对方已经收到了自己的连接建立信息,并完成双方初始序列号的同步。两次握手无法让服务器确认客户端已经收到第二个报文,也更容易受到历史重复报文的干扰。
三次握手过程中状态的变化
在通常的客户端主动打开、服务器被动打开场景中,状态变化如下:
客户端 服务端
CLOSED LISTEN
| |
| -------- SYN ---------------------------> |
SYN-SENT SYN-RECEIVED
| |
| <------ SYN+ACK ------------------------- |
ESTABLISHED SYN-RECEIVED
| |
| -------- ACK ---------------------------> |
ESTABLISHED ESTABLISHED
TCP 也支持同时打开:两个端点都可以主动发送 SYN,双方都可能经历 SYN-SENT 和 SYN-RECEIVED 状态。因此,不能绝对地认为客户端一定是主动打开方、服务器一定是被动打开方。
三次握手的其他常见问题
1. ISN 是固定的吗?
不是。三次握手的一个重要功能是交换客户端和服务端的初始序列号(Initial Sequence Number,ISN),以便双方按照序列号确认和组装后续数据。
如果 ISN 固定或容易预测,攻击者可能猜测后续序列号和确认号,伪造 TCP 报文。现代 TCP 实现通常会使用随连接变化的、具有一定不可预测性的 ISN。ISN 的动态生成主要是为了避免序列号冲突和降低报文伪造风险,并不是简单地“每次随机取一个数”就可以替代完整的 TCP 安全机制。
2. 什么是半连接队列?
服务器收到客户端的 SYN 后,连接还没有完成三次握手,通常会处于 SYN-RECEIVED 状态。很多操作系统会为这类尚未完成握手的请求维护半连接相关的请求队列,因此面试中常称为“半连接队列”或 SYN 队列。
三次握手完成后,连接通常会进入等待应用程序 accept 的队列,面试中常称为“全连接队列”或 accept 队列。半连接队列和全连接队列的具体实现、长度和满载行为取决于操作系统,并不是 TCP 标准规定的统一数据结构。
当队列满时,系统可能丢弃或延迟请求,也可能使用 SYN Cookies 等机制减少半连接状态占用,不能简单概括为“一定丢包”。SYN Flood 攻击正是利用了大量半连接请求消耗服务器资源这一特点。
3. SYN-ACK 会重传多少次?
服务器发送 SYN-ACK 后,如果没有收到客户端最后的 ACK,通常会根据重传定时器重传 SYN-ACK。重传等待时间一般会采用退避策略,常见表现类似 1s、2s、4s、8s,但具体的初始时间、退避规则、最大重传次数和超时时间都由操作系统及配置决定。
达到最大重传次数后,系统通常会清理相关连接状态。启用 SYN Cookies 时,服务器处理半连接请求的方式又会有所不同,因此不能把某一套重传次数当成 TCP 的固定规定。
4. 三次握手过程中可以携带数据吗?
不能简单地说“第一次和第二次不能携带数据,第三次才能携带数据”。TCP 规范允许带有 SYN 标志的报文携带应用数据,SYN、SYN+ACK 和最后的 ACK 都可能携带数据。
在传统的普通 TCP 连接中,很多实现会等握手完成后再发送应用数据,因此面试中常见的简化说法是“第三次握手可以携带数据”。但 TCP Fast Open(TFO)就允许客户端在 SYN 中携带数据,服务端也可能在 SYN+ACK 中返回数据。
握手阶段收到的数据通常需要先由 TCP 协议栈缓存,等连接确认有效后再交付给应用程序,以避免旧连接或伪造报文中的数据被错误处理。
四次挥手
四次挥手同样需要结合状态变化来回答,而不是只背诵“FIN、ACK、FIN、ACK”四个词。
正常关闭时,TCP 连接的两个方向需要分别关闭,因此通常会出现四个报文段。下面假设客户端先发起关闭请求:
- 第一次挥手:客户端发送 FIN 报文,表示客户端不再向服务端发送数据。客户端进入
FIN-WAIT-1状态。 - 第二次挥手:服务端收到 FIN 后发送 ACK,确认号为客户端 FIN 的序列号加 1。服务端进入
CLOSE-WAIT状态,客户端收到 ACK 后进入FIN-WAIT-2状态。 - 第三次挥手:服务端应用程序处理完剩余数据后,也发送 FIN,表示服务端不再发送数据。服务端进入
LAST-ACK状态。 - 第四次挥手:客户端收到服务端 FIN 后发送 ACK,确认号为服务端 FIN 的序列号加 1。正常情况下客户端进入
TIME-WAIT状态。 - 服务端收到最后的 ACK 后进入
CLOSED状态;客户端等待 TIME-WAIT 计时器结束后,也进入CLOSED状态。

四次挥手的状态变化
客户端 服务端
ESTABLISHED ESTABLISHED
| |
| -------- FIN ---------------------------> |
FIN-WAIT-1 CLOSE-WAIT
| |
| <-------- ACK --------------------------- |
FIN-WAIT-2 CLOSE-WAIT
| |
| <-------- FIN --------------------------- |
TIME-WAIT LAST-ACK
| |
| -------- ACK ---------------------------> |
TIME-WAIT CLOSED
| |
CLOSED
客户端发送 FIN 后,只是关闭了客户端到服务端的发送方向,客户端仍然可以接收服务端发送的数据。服务端收到 FIN 后先回复 ACK,但不一定马上发送 FIN;如果服务端应用程序还有数据要发送,就会继续发送,等发送完毕并执行关闭操作后才发送自己的 FIN。这就是 TCP 的半关闭特性。
因此,服务端停留在 CLOSE-WAIT 状态通常表示 TCP 已经收到对方的关闭请求,但本地应用程序还没有关闭自己的发送方向。如果应用程序没有及时关闭连接,CLOSE-WAIT 可能长期存在。
TIME-WAIT 状态
TIME-WAIT 是四次挥手中的高频考点。正常主动关闭的一方在收到对方 FIN、发送最后的 ACK 后进入 TIME-WAIT。它主要有两个作用:
- 确保对方能够收到最后的 ACK:如果服务端没有收到最后的 ACK,它会重传 FIN。客户端在
TIME-WAIT状态下收到重传的 FIN 后,会重新发送 ACK,并重新启动计时器。 - 避免旧连接的延迟报文干扰新连接:为旧连接的报文留出足够的消退时间,避免相同连接四元组(源 IP、源端口、目的 IP、目的端口)被快速复用时,旧报文被误认为是新连接的数据。
TIME-WAIT 的持续时间不是“一次网络往返时间”。TCP 规范要求主动关闭方在 TIME-WAIT 状态保持 2 × MSL(Maximum Segment Lifetime,最长报文段寿命)。RFC 9293 在规范中将 MSL 取为 2 分钟,因此传统教材常写成 4 分钟;具体操作系统可能使用不同的实现时长,不能把 4 分钟理解为所有系统的固定值。
如果在 TIME-WAIT 期间收到对方重传的 FIN,系统会重新发送 ACK,并重新计算或启动 TIME-WAIT 计时器。计时器结束后,连接控制状态才会被清理并进入 CLOSED。
TIME-WAIT 保留的是内核中的连接控制状态和连接四元组,并不意味着本地端口在任何情况下都绝对不能复用。端口能否复用还取决于新的连接四元组、操作系统策略和套接字选项。
四次挥手并不总是四个报文
如果服务端收到客户端 FIN 后已经没有数据要发送,并且应用程序立即关闭连接,那么服务端可以把第二次挥手的 ACK 和第三次挥手的 FIN 合并成一个 FIN+ACK 报文,整个过程通常只需要三个报文段:
客户端 → 服务端:FIN
服务端 → 客户端:FIN+ACK
客户端 → 服务端:ACK
这时,客户端可能从 FIN-WAIT-1 直接进入 TIME-WAIT,跳过 FIN-WAIT-2。但如果双方同时发送 FIN,可能进入 CLOSING 状态,并且双方都可能进入 TIME-WAIT。如果使用 RST 强制重置连接,则属于异常关闭,不是正常的四次挥手。
TCP 状态含义
下面是常见 TCP 状态的含义:
LISTEN:等待来自远方 TCP 端口的连接请求。
SYN-SENT:已经发送连接请求,等待匹配的连接请求或响应。
SYN-RECEIVED:已经收到并发送连接请求,等待对方对连接请求的确认。
ESTABLISHED:连接已经建立,双方可以传输数据。
FIN-WAIT-1:等待远程 TCP 的连接终止请求,或等待对本地 FIN 的确认。
FIN-WAIT-2:本地 FIN 已经得到确认,等待远程 TCP 的连接终止请求。
CLOSE-WAIT:已经收到远程 FIN,等待本地应用程序关闭连接的发送方向。
CLOSING:双方同时关闭,等待远程 TCP 对本地 FIN 的确认。
LAST-ACK:已经发送本地 FIN,等待远程 TCP 对该 FIN 的确认。
TIME-WAIT:等待足够长的时间,以确保远程 TCP 收到终止连接的 ACK,并避免旧报文影响新连接。
CLOSED:没有活动的连接状态。

延伸阅读
计算机网络和操作系统在面试中被问到的概率也比较高,下面保留一些相关资料:
- 计算机网络五层模型入门
- 通信双方如何保证消息不丢失?
- 集线器、交换机与路由器有什么区别?
- 什么是 TCP 拥塞控制?
- 什么是 TCP 流量控制
- 什么是 TCP 三次握手?
- 什么是 TCP 四次挥手?
- 什么是 HTTP?
- 什么是 HTTPS?
- 什么是 SSL/TLS 协议?
- 什么是 DNS?
- 什么是 DHCP?
- 什么是广播路由算法?
- 什么是数字签名?
- 什么是 SQL 注入攻击?
- 什么是 XSS 攻击?
作者:帅地
原文链接:https://juejin.cn/post/6844903834708344840
来源:稀土掘金。著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。