TCP三次握手和四次挥手详解
一、TCP协议简介
TCP(Transmission Control Protocol,传输控制协议)是一种面向连接的、可靠的、基于字节流的传输层通信协议。它位于OSI七层模型中的第四层——传输层,负责在两台主机之间提供可靠的数据传输服务。
TCP的核心特点:
- 面向连接:通信前必须先建立连接,通信结束后必须释放连接
- 可靠传输:通过确认、重传、流量控制等机制保证数据可靠到达
- 全双工:连接建立后,双方可以同时发送和接收数据
- 基于字节流:数据被看作一连串的字节流,没有报文边界
二、为什么需要三次握手?
2.1 什么是三次握手?
三次握手(Three-way Handshake)是TCP建立连接的过程,需要客户端和服务器之间交换三个报文段才能完成连接的建立。
可以把它理解成两个人打电话的过程:
- A打电话给B:”喂,能听到我说话吗?”
- B回答:”能听到,你能听到我吗?”
- A回答:”能听到,那我们开始聊天吧”
2.2 三次握手的详细过程
第一次握手(SYN):
- 客户端向服务器发送SYN报文段(SYN=1)
- 客户端选择一个初始序列号seq=x
- 客户端进入SYN_SENT状态
- 此时客户端还没有收到服务器的确认
第二次握手(SYN+ACK):
- 服务器收到SYN报文后,回复SYN+ACK报文
- SYN=1表示服务器也同意建立连接
- ACK=1表示确认收到了客户端的SYN
- 服务器选择自己的初始序列号seq=y
- ack=x+1表示期望收到的下一个字节的序号
- 服务器进入SYN_RCVD状态
第三次握手(ACK):
- 客户端收到SYN+ACK后,回复ACK报文
- ACK=1表示确认收到了服务器的SYN
- ack=y+1表示期望收到的下一个字节的序号
- seq=x+1(因为SYN占用一个序号)
- 客户端进入ESTABLISHED状态
- 服务器收到ACK后,也进入ESTABLISHED状态
2.3 为什么是三次,不是两次或四次?
为什么不能是两次握手?
如果只有两次握手,可能会出现这样的问题:
- 客户端发送的第一个SYN报文因为网络延迟迟迟未到
- 客户端超时重发了一个SYN,服务器收到后建立连接
- 数据传输完成后,连接释放
- 这时第一个迟到的SYN才到达服务器
- 服务器以为是新的连接请求,又建立了一个连接
- 但客户端并没有发起新连接的意愿,不会发送数据
- 这样就白白浪费了服务器的资源
三次握手可以防止这种”已失效的连接请求报文段”突然又传送到服务器,从而产生错误。
为什么不需要四次握手?
因为第二次握手时,服务器可以把SYN和ACK放在同一个报文里发送,不需要分开。三次握手已经足够确认双方的发送和接收能力都正常,再多一次就是浪费了。
2.4 三次握手确认了什么?
通过三次握手,双方都确认了:
- 自己的发送能力正常
- 自己的接收能力正常
- 对方的发送能力正常
- 对方的接收能力正常
| 握手次数 | 客户端确认 | 服务器确认 |
|---|---|---|
| 第一次 | – | 客户端发送能力正常 |
| 第二次 | 服务器发送、接收能力正常 | – |
| 第三次 | – | 客户端接收能力正常 |
三、为什么需要四次挥手?
3.1 什么是四次挥手?
四次挥手(Four-way Handshake)是TCP释放连接的过程,需要客户端和服务器之间交换四个报文段才能完成连接的释放。
因为TCP是全双工的,所以关闭连接需要双方各自关闭自己的发送通道。
3.2 四次挥手的详细过程
第一次挥手(FIN):
- 客户端发送FIN报文段(FIN=1)
- seq=u(客户端当前数据的最后一个字节序号+1)
- 客户端进入FIN_WAIT_1状态
- 表示客户端没有数据要发送了,请求关闭连接
第二次挥手(ACK):
- 服务器收到FIN后,回复ACK报文
- ACK=1,ack=u+1
- seq=v
- 服务器进入CLOSE_WAIT状态
- 客户端收到ACK后,进入FIN_WAIT_2状态
- 此时客户端到服务器的方向已经关闭,但服务器还可以继续发送数据
第三次挥手(FIN):
- 服务器发送完所有数据后,发送FIN报文
- FIN=1,seq=w
- 服务器进入LAST_ACK状态
- 表示服务器也没有数据要发送了
第四次挥手(ACK):
- 客户端收到FIN后,回复ACK报文
- ACK=1,ack=w+1
- seq=u+1
- 客户端进入TIME_WAIT状态
- 服务器收到ACK后,进入CLOSED状态
- 客户端等待2MSL后,也进入CLOSED状态
3.3 为什么建立连接是三次,释放连接是四次?
建立连接时,服务器的SYN和ACK可以放在同一个报文里发送,所以是三次。
但释放连接时,服务器收到客户端的FIN后,可能还有数据没有发送完,所以不能立即发送FIN。只能先回复ACK确认收到了FIN,等自己的数据都发完了,再发送FIN。所以ACK和FIN要分开发送,就变成了四次。
简单说:
- 建立连接:SYN和ACK可以合并 → 三次
- 释放连接:ACK和FIN不能合并(可能还有数据要发)→ 四次
3.4 TIME_WAIT状态的作用
客户端在最后一次回复ACK后,并不是直接关闭连接,而是要等待2MSL(Maximum Segment Lifetime,最长报文段寿命)的时间。
为什么需要TIME_WAIT?
保证最后一个ACK能到达服务器
- 如果最后一个ACK丢失了,服务器会超时重发FIN
- 客户端需要在TIME_WAIT状态等待,以便重发ACK
- 如果没有TIME_WAIT,客户端直接关闭了,就收不到重发的FIN,服务器就无法正常关闭
防止”已失效的连接请求报文段”出现在新连接中
- 等待2MSL可以让本次连接产生的所有报文段都从网络中消失
- 这样下一个新的连接就不会收到旧连接的残留报文
MSL是多少?
- RFC 793中定义MSL为2分钟
- 实际实现中常用的值是30秒、1分钟或2分钟
- 所以TIME_WAIT通常是1分钟、2分钟或4分钟
四、常见问题与面试考点
4.1 SYN洪水攻击是什么?
SYN洪水攻击(SYN Flood)是一种常见的DDoS攻击方式:
- 攻击者伪造大量不存在的IP地址,向服务器发送SYN报文
- 服务器回复SYN+ACK后,需要等待客户端回复ACK
- 因为源地址是假的,服务器永远等不到ACK
- 服务器会维护大量的半连接队列,直到资源耗尽
- 正常用户的连接请求就无法被处理了
防御方法:
- SYN Cookie:不保存半连接状态,而是用Cookie机制验证
- 增加半连接队列的大小
- 缩短SYN超时时间
- 防火墙过滤
4.2 为什么不能用一次挥手关闭连接?
因为TCP是全双工的,双方都需要独立关闭自己的发送通道。如果只有一次挥手,只能关闭一个方向,另一个方向还开着,就不完整。
4.3 如果已经建立了连接,客户端突然故障了怎么办?
TCP有一个保活计时器(Keepalive Timer):
- 服务器每隔一段时间(通常是2小时)发送一个探测报文
- 如果连续几次(通常是10次)都没有收到回复
- 服务器就认为客户端出故障了,然后关闭连接
不过这个时间间隔比较长,实际应用中通常会在应用层自己实现心跳机制。
4.4 三次握手过程中可以携带数据吗?
可以,但通常不这样做。
- 第一次握手的SYN报文:不能携带数据。因为还没有建立连接,携带数据可能被滥用进行攻击。
- 第三次握手的ACK报文:可以携带数据。因为此时连接已经快要建立了,客户端可以把第一批数据放在这个ACK里一起发过去,节省一个报文。
五、总结
TCP的三次握手和四次挥手是网络编程中最基础也最重要的概念之一:
三次握手的核心目的:
- 确认双方的收发能力都正常
- 同步双方的初始序列号
- 防止已失效的连接请求造成错误
四次挥手的核心目的:
- 可靠地关闭全双工连接
- 保证双方的数据都能完整发送
- TIME_WAIT确保最后一个ACK能到达,且旧报文不会影响新连接
理解了这些机制,你就能更好地排查网络问题,也能在面试中从容应对相关的问题。