目录
第一部分:核心概念与背景
第二部分:三次握手 — 建立连接
第三部分:四次挥手 — 关闭连接
总结与对比
面试回答
第一部分:核心概念与背景
在开始之前,先明确几个关键点:
-
- SYN:同步序列号,用于建立连接。
- ACK:确认,表示确认号字段有效。
- FIN:终止,用于关闭连接。
-
- 序列号:标识发送的数据字节流的顺序。
- 确认号:期望收到对方下一个报文段的第一个数据字节的序列号,表示此序号之前的数据已成功接收。
第二部分:三次握手 — 建立连接
三次握手的根本目的是:同步双方的初始序列号,并交换其他参数,为可靠数据传输做准备。同时,它也证明了双方都具有发送和接收的能力。
假设客户端(Client)主动发起连接,服务器(Server)等待连接。
第一步:第一次握手(Client -> Server)
- 客户端发送一个 TCP 报文段。
- 设置标志位 SYN = 1,表示这是连接请求。
- 同时,客户端会随机生成一个初始序列号 client_isn,放在序列号字段中。
- 此时不携带应用层数据。
- 客户端状态:从 CLOSED 进入 SYN-SENT(同步已发送)。
第二步:第二次握手(Server -> Client)
- 服务器收到 SYN 报文后,如果同意建立连接,则会回复一个报文段。
- 设置标志位 SYN = 1 和 ACK = 1。
- 服务器也会随机生成自己的初始序列号 server_isn,放在序列号字段中。
- 确认号字段设置为 client_isn + 1,表示“我已收到你的序列号为 client_isn 的 SYN 包,期待下一个数据从 client_isn + 1 开始”。
- 此时可以携带或不携带应用层数据(但通常不携带)。
- 服务器状态:从 LISTEN 进入 SYN-RCVD(同步已收到)。
第三步:第三次握手(Client -> Server)
- 客户端收到服务器的 SYN-ACK 报文后,会再发送一个确认报文。
- 设置标志位 ACK = 1。
- 序列号字段设置为 client_isn + 1(因为第一次握手消耗了一个序列号)。
- 确认号字段设置为 server_isn + 1,表示“我已收到你的序列号为 server_isn 的 SYN 包”。
- 此报文可以携带应用层数据(连接建立后即可开始传输)。
- 客户端状态:从 SYN-SENT 进入 ESTABLISHED(已建立连接)。
- 服务器收到这个 ACK 后,状态也从 SYN-RCVD 进入 ESTABLISHED。
为什么是三次,而不是两次?
- 根本原因:防止已失效的连接请求报文突然到达,导致资源浪费和错误。
- 经典场景:客户端发送了一个 SYN 报文,但由于网络拥堵,迟迟未到达服务器。客户端超时重传了一个新的 SYN 并成功建立了连接、传输数据、关闭连接。此时,那个失效的旧 SYN 报文终于到达了服务器。如果只用两次握手,服务器会认为这是一个新的连接请求,直接进入 ESTABLISHED 状态,并分配资源等待客户端发送数据,但客户端早已关闭,不会发送任何数据。这就造成了服务器的资源被白白占用。
- 三次握手的作用:在两次握手的情况下,服务器会直接建立连接。而采用三次握手,服务器在收到失效的 SYN 后,会回复 SYN-ACK,但客户端(因为已经关闭)不会回复最终的 ACK,因此服务器在 SYN-RCVD 状态等待一段时间后,会因收不到 ACK 而关闭这个半连接,从而避免资源浪费。
第三部分:四次挥手 — 关闭连接
四次挥手的目的是:双方都确认没有数据需要发送了,然后安全地关闭连接。由于 TCP 连接是全双工的(可以同时独立地进行双向数据传输),因此每个方向都必须单独关闭。
假设客户端主动发起关闭。
第一步:第一次挥手(Client -> Server)
- 客户端应用程序调用 close() 方法,发送一个 TCP 报文段。
- 设置标志位 FIN = 1。
- 携带一个当前的序列号 seq = u。
- 客户端状态:从 ESTABLISHED 进入 FIN-WAIT-1(终止等待1)。
第二步:第二次挥手(Server -> Client)
- 服务器收到 FIN 报文后,立即回复一个确认报文。
- 设置标志位 ACK = 1。
- 确认号设置为 u + 1。
- 此时,从客户端到服务器方向的连接就关闭了。客户端不会再向服务器发送数据,但服务器可能还有数据要发送给客户端,连接处于半关闭状态。
- 服务器状态:从 ESTABLISHED 进入 CLOSE-WAIT(关闭等待)。
- 客户端状态:收到这个 ACK 后,从 FIN-WAIT-1 进入 FIN-WAIT-2(终止等待2)。
第三步:第三次挥手(Server -> Client)
- 当服务器将剩余数据全部发送完毕后,它的应用程序也会调用 close() 方法,发送一个 FIN 报文。
- 设置标志位 FIN = 1(通常还会同时设置 ACK = 1)。
- 携带一个序列号 seq = w。
- 服务器状态:从 CLOSE-WAIT 进入 LAST-ACK(最后确认)。
第四步:第四次挥手(Client -> Server)
- 客户端收到服务器的 FIN 报文后,必须发送一个确认报文。
- 设置标志位 ACK = 1。
- 确认号设置为 w + 1。
- 客户端状态:从 FIN-WAIT-2 进入 TIME-WAIT(时间等待)。此时客户端连接尚未完全关闭,需要等待 2MSL(两倍的最大报文段生存时间)时长。
- 服务器收到这个 ACK 后,状态从 LAST-ACK 进入 CLOSED,连接正式关闭。
为什么客户端需要 TIME-WAIT 状态?等待 2MSL 的目的是什么?
总结与对比
|
阶段 |
目的 |
关键标志位 |
状态变化 |
|
三次握手 |
建立连接,同步初始序列号 |
SYN, ACK |
CLOSED -> SYN-SENT -> SYN-RCVD -> ESTABLISHED |
|
第一次挥手 |
客户端发起关闭(关闭己方发送通道) |
FIN |
ESTABLISHED -> FIN-WAIT-1 |
|
第二次挥手 |
服务器确认收到关闭请求 |
ACK |
ESTABLISHED -> CLOSE-WAIT / FIN-WAIT-1 -> FIN-WAIT-2 |
|
第三次挥手 |
服务器发起关闭(关闭己方发送通道) |
FIN, ACK |
CLOSE-WAIT -> LAST-ACK |
|
第四次挥手 |
客户端确认收到关闭请求 |
ACK |
FIN-WAIT-2 -> TIME-WAIT -> CLOSED |
面试回答
三次握手(建立连接)
目的:为了确认双方的发送和接收能力都正常,并同步初始序列号(ISN)。
四次挥手(断开连接)
目的:双方都确认数据已发送完毕,可以安全关闭连接。之所以是四次,因为TCP连接是全双工的,每一方向都需要独立关闭。
所以总结一下,握手是三次,因为第二次的回复合并了确认和发起请求。而挥手是四次,因为服务器在收到关闭请求后,可能还有数据要发送,所以把确认和发起自己的关闭请求分成了两步,没办法合并。
追加:为什么握手是三次,不是两次?
主要是为了防止已失效的连接请求报文突然又传到服务器,导致服务器错误地打开连接。三次握手机制下,这个失效的报文会让客户端收到一个它并未请求的确认,客户端会拒绝,服务器收不到第三次ACK,就不会建立连接。
追加:为什么挥手需要四次?
因为TCP是全双工的。当一方说“我要关了”(发FIN),只代表它不再发送数据,但还可以接收数据。另一方可能还有数据要发送,所以必须先立刻回复一个ACK,等自己数据发完再发FIN。因此ACK和FIN在多数情况下不能像握手那样合并发送。
追加: TIME_WAIT状态是什么?为什么需要等待2MSL?
主动关闭的一方(先发FIN的,比如刚才例子里的客户端)在发送完最后一个ACK后,会进入TIME_WAIT状态,等待2倍的最大报文段生存时间(2MSL) 两个目的:
如果小假的内容对你有帮助,请点赞,评论,收藏。创作不易,大家的支持就是我坚持下去的动力!

网硕互联帮助中心





评论前必须登录!
注册