一、先搞懂背景:TCP 是个什么东西?
TCP 是一个可靠的、面向连接的协议。
- 就像打电话:你和对方都要先 "喂?听得见吗?" 确认线路没问题,再开始说话,说完还要互相确认 "我挂了",避免对方还在等。
- 三次握手就是建立连接的过程 ,四次挥手就是断开连接的过程。
二、三次握手:建立连接(为什么是三次,不是两次?)
核心目标
让客户端和服务器,互相确认三件事:
- 我的发送能力没问题
- 我的接收能力没问题
- 对方的发送和接收能力也没问题
角色分工
- 客户端:主动发起连接的一方(比如你的浏览器)
- 服务器:被动等待连接的一方(比如网站的服务器)
三次握手的完整流程(带对话场景)
假设客户端叫 A,服务器叫 B。
第一次握手:A → B(SYN)
A 给 B 打电话:
"喂,B 吗?我是 A,我想跟你连个线,我的初始序号是 x,你能听到我说话吗?"
- 对应报文:
SYN=1,seq=x - 服务器收到后:知道了 "A 的发送能力没问题,B 的接收能力也没问题",但 B 还不知道 A 能不能听到自己说话。
第二次握手:B → A(SYN+ACK)
B 给 A 回电话:
"我听到你了 A!你的序号 x 我收到了,我确认收到你的请求(ACK=x+1)。我也想跟你连线,我的初始序号是 y,你能听到我说话吗?"
- 对应报文:
SYN=1,ACK=1,ack=x+1,seq=y - 客户端收到后:知道了 "B 的发送和接收能力都没问题,A 的接收能力也没问题"。
第三次握手:A → B(ACK)
A 再回 B 一句:
"收到你的回应啦!我也听到你说话了,确认你的序号 y(ACK=y+1),我们可以开始传数据了!"
- 对应报文:
ACK=1,ack=y+1 - 服务器收到后:终于确认 "A 的接收能力没问题",双方正式建立连接。
为什么不能两次握手?
如果只有两次,B 发完第二次握手的包后,就以为连接建立成功了,开始给 A 发数据。但如果 A 没收到这个包,A 根本不知道 B 要连,就会忽略 B 的数据,B 这边就会一直等 A 回应,白白浪费资源。三次握手就是为了双向确认双方都收到了对方的信号,避免这种 "单向确认" 的问题。
三、四次挥手:断开连接(为什么是四次?)
TCP 是全双工的,意思是:A 和 B 可以同时互相发数据,像双向车道。断开连接时,两边都要确认自己这边的 "车道" 关了,所以需要四次挥手。
核心目标
双方都确认:
- 对方不会再给我发数据了
- 我这边也发完了,可以安全断开了
四次挥手的完整流程(带对话场景)
还是 A(客户端)和 B(服务器)。
第一次挥手:A → B(FIN)
A 说:
"B,我这边的数据发完了,我不想再给你发数据了,我准备关我的发送通道了(FIN),你收到了吗?"
- 对应报文:
FIN=1,seq=u - B 收到后:知道 A 不会再发数据了,但 B 可能还有数据没发给 A,所以 B 先回个确认,不马上关自己的通道。
第二次挥手:B → A(ACK)
B 回 A:
"收到了,我知道你不发数据了,我先确认一下(ACK=u+1),我这边还有点数据要发给你,等我发完再跟你说。"
- 对应报文:
ACK=1,ack=u+1 - 此时,A 到 B 的方向(A 的发送通道)关闭了,但 B 到 A 的方向还能继续传数据。
第三次挥手:B → A(FIN)
B 把剩下的数据发完了,跟 A 说:
"A,我这边的数据也发完了,我也不想再给你发数据了,我准备关我的发送通道了(FIN),你收到了吗?"
- 对应报文:
FIN=1,ACK=1,seq=v,ack=u+1 - A 收到后,知道 B 也发完了,准备回个确认。
第四次挥手:A → B(ACK)
A 回 B:
"收到了,我知道你不发数据了,确认一下(ACK=v+1),你可以关了。"
- 对应报文:
ACK=1,ack=v+1 - 服务器收到后,立刻关闭连接。客户端这边会等一段时间(TIME_WAIT),确保 B 收到了这个确认,避免 B 没收到,一直等不到回应。
为什么挥手是四次,握手却是三次?
因为握手的时候,B 可以把 "确认 A 的请求" 和 "向 A 发起连接请求" 合并成一个包(SYN+ACK),所以两次合并成了一次,总共三次。但挥手的时候,B 收到 A 的 FIN 后,可能还有数据没发完,不能马上回 FIN,必须先回 ACK 确认,等数据发完了再发 FIN,所以 ACK 和 FIN 必须分成两次发,总共四次。
四、关键补充:几个容易搞混的点
- SYN、ACK、FIN 是什么?
SYN:同步位,用来发起连接请求ACK:确认位,用来告诉对方 "我收到你的包了"FIN:结束位,用来发起断开请求
- 序号 seq 和确认号 ack 的作用
seq:我这次发的包的序号ack:我收到了你序号为 x 的包,下次请从 x+1 开始发- 这两个序号是 TCP 保证数据不丢、不乱序的核心。
- **TIME_WAIT 为什么要等?**因为第四次挥手的 ACK 如果丢了,B 收不到,就会一直重发 FIN 请求。A 等一会儿,是为了确保 B 能收到 ACK,避免 B 一直卡在断开状态。