1. 三次握手详细流程与状态变迁
1.1 握手目的
TCP三次握手是建立可靠连接的过程,主要目的:
-
确认双方收发能力正常
-
同步双方序列号
-
协商连接参数
1.2 详细流程
**第一次握手(SYN)**
```
客户端 → 服务器
TCP段:
-
Source Port: 12345 (客户端随机端口)
-
Destination Port: 80 (HTTP服务端口)
-
Sequence Number: 1000 (客户端初始序列号ISN)
-
Flags: SYN (同步标志位)
-
Window Size: 65535
状态变化:
客户端:CLOSED → SYN_SENT
```
**第二次握手(SYN+ACK)**
```
服务器 → 客户端
TCP段:
-
Source Port: 80
-
Destination Port: 12345
-
Sequence Number: 2000 (服务器初始序列号ISN)
-
Acknowledgment Number: 1001 (确认客户端序列号+1)
-
Flags: SYN+ACK (同步+确认)
状态变化:
服务器:LISTEN → SYN_RCVD
```
**第三次握手(ACK)**
```
客户端 → 服务器
TCP段:
-
Source Port: 12345
-
Destination Port: 80
-
Sequence Number: 1001
-
Acknowledgment Number: 2001 (确认服务器序列号+1)
-
Flags: ACK (确认标志位)
状态变化:
客户端:SYN_SENT → ESTABLISHED
服务器:SYN_RCVD → ESTABLISHED
```
1.3 状态变迁图
```
客户端状态:
CLOSED → SYN_SENT → ESTABLISHED
服务器状态:
CLOSED → LISTEN → SYN_RCVD → ESTABLISHED
```
2. 为什么要三次握手,两次为什么不行
2.1 两次握手的问题
**场景分析**:
```
情况1:延迟的SYN报文
客户端发送SYN → 网络延迟 → 服务器回复SYN+ACK → 客户端已超时关闭
延迟的SYN到达服务器 → 服务器以为是新连接 → 发送SYN+ACK
如果两次握手就建立连接,服务器会维护一个不存在的连接
情况2:序列号不同步
只有两次握手,服务器无法确认客户端收到了自己的序列号
可能导致数据传输时序列号不匹配
```
2.2 三次握手的必要性
**确保双向通信能力**:
```
第一次握手:客户端→服务器,证明客户端能发
第二次握手:服务器→客户端,证明服务器能收能发
第三次握手:客户端→服务器,证明客户端能收
只有三次握手,才能确认双方收发能力都正常
```
**序列号同步**:
```
三次握手过程中,双方交换序列号并确认
确保双方对数据位置有共同认知
为后续可靠传输奠定基础
```
2.3 实例:两次握手的风险
```
假设只需要两次握手:
步骤1:客户端发送SYN(seq=1000)
步骤2:服务器发送SYN+ACK(seq=2000, ack=1001)
连接建立
问题:
如果步骤1的SYN延迟到达,服务器会建立连接
但客户端可能已经关闭,造成资源浪费
服务器会一直等待客户端数据,直到超时
```
3. 四次挥手为什么要TIME_WAIT
3.1 四次挥手流程
**第一次挥手(FIN)**
```
主动关闭方 → 被动关闭方
Flags: FIN
Sequence Number: 5000
```
**第二次挥手(ACK)**
```
被动关闭方 → 主动关闭方
Flags: ACK
Acknowledgment Number: 5001
```
**第三次挥手(FIN)**
```
被动关闭方 → 主动关闭方
Flags: FIN
Sequence Number: 6000
```
**第四次挥手(ACK)**
```
主动关闭方 → 被动关闭方
Flags: ACK
Acknowledgment Number: 6001
```
3.2 TIME_WAIT的作用
**等待最后一个ACK被确认**:
```
主动关闭方发送最后一个ACK后进入TIME_WAIT状态
如果被动关闭方没收到ACK,会重传FIN
TIME_WAIT状态确保有时间重传丢失的ACK
```
**防止旧连接的延迟报文干扰新连接**:
```
网络中可能存在延迟的TCP报文
TIME_WAIT等待足够长时间,让这些报文过期
确保新连接不会收到旧连接的报文
```
3.3 TIME_WAIT状态特点
```
持续时间:2MSL(Maximum Segment Lifetime)
典型值:1-2分钟
占用资源:端口号在TIME_WAIT期间无法被重用
```
4. TIME_WAIT为什么是2MSL
4.1 MSL的定义
MSL(Maximum Segment Lifetime):一个TCP报文在网络中的最大生存时间。
```
RFC标准建议MSL为2分钟
实际实现中常见30秒、1分钟
```
4.2 为什么是2倍MSL
**等待往返时间**:
```
发送最后一个ACK后,最多等待MSL时间让ACK到达对方
如果对方没收到ACK,会在MSL时间内重传FIN
主动关闭方需要再等待MSL时间接收重传的FIN
总共需要2MSL时间
```
**公式说明**:
```
2MSL = MSL(ACK传输) + MSL(可能的FIN重传)
确保:
-
最后一个ACK有足够时间到达对方
-
对方重传的FIN有足够时间到达
-
所有延迟报文都已过期
```
4.3 实例:2MSL的必要性
```
场景:最后一个ACK丢失
步骤1:主动关闭方发送ACK(ack=6001)
步骤2:ACK丢失
步骤3:被动关闭方超时,重传FIN(seq=6000)
步骤4:主动关闭方在TIME_WAIT期间收到FIN
步骤5:主动关闭方重传ACK(ack=6001)
步骤6:被动关闭方收到ACK,连接正常关闭
如果没有TIME_WAIT或时间不足:
步骤4:主动关闭方已关闭,无法接收FIN
步骤5:被动关闭方无法收到ACK
结果:被动关闭方资源无法释放
```
5. SYN泛洪攻击与半连接队列
5.1 SYN泛洪攻击原理
攻击者发送大量SYN请求,但不完成三次握手:
```
攻击流程:
-
攻击者发送SYN(seq=X)
-
服务器回复SYN+ACK(seq=Y, ack=X+1)
-
攻击者不发送ACK
-
服务器进入SYN_RCVD状态,等待ACK
-
大量半连接耗尽服务器资源
```
5.2 半连接队列
**定义**:
```
服务器维护的等待三次握手完成的连接队列
队列满时,新的SYN请求会被丢弃或拒绝
```
**队列状态**:
```
SYN_RCVD状态的连接在半连接队列中
等待第三次握手的ACK
超时时间通常为1分钟左右
```
5.3 防御措施
**SYN Cookies**:
```
原理:服务器不保存半连接状态
使用cookie代替队列
cookie = hash(客户端IP, 客户端端口, 服务器IP, 服务器端口, 时间戳)
流程:
-
收到SYN,计算cookie作为序列号
-
发送SYN+ACK(seq=cookie)
-
收到ACK时验证cookie
-
验证通过才建立连接
```
**增加队列长度**:
```
调整内核参数:
tcp_max_syn_backlog - 半连接队列最大长度
tcp_syncookies - 启用SYN Cookies
tcp_synack_retries - SYN+ACK重传次数
```
**限流和过滤**:
```
-
使用防火墙限制每秒SYN数量
-
基于源IP限流
-
识别攻击流量并阻断
```
5.4 实例:SYN泛洪攻击与防御
```
攻击场景:
攻击者每秒发送10000个SYN请求
服务器半连接队列长度=1024
队列很快填满
正常用户无法建立连接
防御后:
启用SYN Cookies
服务器不再维护半连接队列
攻击者的SYN请求无法消耗资源
正常用户可以正常连接
```
6. 总结
TCP三次握手和四次挥手是保证可靠连接的核心机制:
-
**三次握手**:确认双方通信能力,同步序列号
-
**两次握手不够**:无法确认双向通信能力,存在安全风险
-
**TIME_WAIT**:等待最后ACK确认,防止旧报文干扰
-
**2MSL**:确保往返时间内的报文都能处理
-
**SYN泛洪防御**:使用SYN Cookies和队列优化
理解这些机制对于网络编程、故障排查和安全防护至关重要。在实际应用中,需要合理配置系统参数,平衡性能和安全性。