TCP三次握手四次挥手
tcp三次握手
TCP(传输控制协议)的三次握手是建立可靠连接的关键过程,用于在客户端和服务器之间同步序列号并交换控制信息。
三次握手的过程(简化版)
第一次握手(SYN)
- 发送方: 客户端
- 数据包内容: 客户端发送一个 TCP 数据包,设置 SYN(同步)标志位为 1,并携带一个初始序列号(Sequence Number),通常记为
Seq = x
。 - 含义: 表示客户端请求建立连接,并告诉服务器客户端的初始序列号。
- 状态: 客户端进入
SYN_SENT
状态。
第二次握手(SYN+ACK)
- 发送方: 服务器
- 数据包内容: 服务器回应一个 TCP 数据包,设置 SYN 标志位为 1 和 ACK(确认)标志位为 1。
- 服务器的初始序列号为
Seq = y
。 - 确认号(Acknowledgment Number)为
Ack = x + 1
,表示服务器已接收到客户端的 SYN 并期待客户端的下一个数据包序列号。
- 服务器的初始序列号为
- 含义: 服务器同意建立连接,同时告知客户端自己的初始序列号。
- 状态: 服务器进入
SYN_RCVD
状态。
第三次握手(ACK)
- 发送方: 客户端
- 数据包内容: 客户端发送一个 TCP 数据包,设置 ACK 标志位为 1。
- 序列号
Seq = x + 1
(紧接第一次握手的序列号)。 - 确认号
Ack = y + 1
,表示客户端已接收到服务器的 SYN 并确认。
- 序列号
- 含义: 客户端确认服务器的响应,连接正式建立。
- 状态: 客户端和服务器都进入
ESTABLISHED
状态。
数据包示例(简化为关键字段)
假设客户端初始序列号为 1000,服务器初始序列号为 2000:
- 客户端 -> 服务器:
- SYN = 1, Seq = 1000
- 服务器 -> 客户端:
- SYN = 1, ACK = 1, Seq = 2000, Ack = 1001
- 客户端 -> 服务器:
- ACK = 1, Seq = 1001, Ack = 2001
总结
三次握手中的数据包通过 SYN 和 ACK 标志位以及序列号、确认号的交换,确保双方能够可靠地初始化通信。完成后,TCP 连接建立,后续数据传输即可开始。
抓包分析三次握手
- sudo tcpdump -i en0
- wireshark
1.src -> dst syn(syn_sent)
- flag=syn syn_sent(src)
- SEQ=0 相对序列号
- Seq=x0577 原始序列号
2.dst -> src syn + ack(syn_rcvd)
- flag:sync + ack syn_rcvd(dst)
- SEQ=0, Seq=x0888
- Ack=0578
3. src -> dst ack(established)
- flag: ack established(src/dst)
- ACK=1
- Seq=x0578 Ack=0x889
三次握手详解步骤
第一次握手(SYN,客户端 → 服务器)
- 客户端发送一个 SYN(同步)报文给服务器。
- 这个报文中包含客户端的初始序列号(Sequence Number,通常记为
seq = x
),表示客户端数据流的起始点。 - 标志位:
SYN = 1
,表示请求建立连接。 - 此时客户端进入 SYN_SENT 状态,等待服务器的响应。
第二次握手(SYN + ACK,服务器 → 客户端)
- 服务器收到客户端的 SYN 后,回复一个 SYN + ACK 报文。
- 这个报文中:
- SYN = 1:表示服务器也请求同步。
- ACK = 1:确认收到客户端的 SYN。
- 服务器的初始序列号(
seq = y
),表示服务器数据流的起始点。 - 确认号(Acknowledgment Number,记为
ack = x + 1
),表示服务器期望接收客户端的下一个序列号。
- 服务器进入 SYN_RCVD(同步收到)状态。
第三次握手(ACK,客户端 → 服务器)
- 客户端收到服务器的 SYN + ACK 后,发送一个 ACK 报文确认。
- 这个报文中:
- ACK = 1:确认收到服务器的 SYN。
- 序列号(
seq = x + 1
),与服务器的确认号对应。 - 确认号(
ack = y + 1
),表示客户端期望接收服务器的下一个序列号。
- 客户端进入 ESTABLISHED(连接建立)状态。
- 服务器收到这个 ACK 后,也进入 ESTABLISHED 状态。
图示
客户端 服务器
| ---- SYN (seq=x) ----> |
| <--- SYN+ACK (seq=y, ack=x+1) --- |
| ---- ACK (seq=x+1, ack=y+1) ---> |
| [连接建立] |
为什么要三次握手?
三次握手的主要目的是:
- 确认双向通信能力:确保客户端和服务器都能发送和接收数据。
- 同步序列号:双方交换初始序列号(x 和 y),为后续数据传输建立可靠的顺序。
- 防止旧连接干扰:避免因网络延迟导致的过期 SYN 报文误建立连接。
举个生活中的类比
想象你在打电话:
- 你拨号并说:“喂,你能听见我吗?”(第一次握手,SYN)
- 对方的电话接通,说:“能听见,你能听见我吗?”(第二次握手,SYN + ACK)
- 你回答:“能听见,开始聊吧!”(第三次握手,ACK)
常见问题
- 能不能用两次握手? 不行。两次握手只能确认客户端到服务器的通信,服务器到客户端的确认无法保证,可能导致单向连接或旧连接干扰。
- 三次握手中途失败会怎样? 如果某一步失败(比如服务器没回应),客户端会超时重试(通常基于 TCP 重传机制),最终放弃并关闭连接尝试。
查看三次握手
在 Linux 中可以用 tcpdump
或 wireshark
抓包观察:
1 | sudo tcpdump -i eth0 port 80 |
- 假设你在监听 80 端口的 HTTP 连接,就能看到 SYN、SYN+ACK 和 ACK 的报文。
四次挥手关闭
TCP 的四次挥手(Four-Way Handshake)是终止 TCP 连接的过程,用于确保客户端和服务器双方都能安全地关闭连接,并确认所有数据都已被正确传输。以下是 TCP 四次挥手的详细过程,包括每个数据包的角色和状态变化。
四次挥手的过程
TCP 四次挥手涉及客户端和服务器之间的四次数据包交换,标志位(如 FIN 和 ACK)用于控制连接的关闭。假设客户端主动发起关闭,以下是步骤:
第一次挥手(FIN)
- 发送方: 客户端
- 数据包内容: 客户端发送一个 TCP 数据包,设置 FIN(结束)标志位为 1,序列号为
Seq = x
。 - 含义: 客户端表示自己没有更多数据要发送,请求关闭连接。
- 状态: 客户端进入
FIN_WAIT_1
状态。
第二次挥手(ACK)
- 发送方: 服务器
- 数据包内容: 服务器回应一个 TCP 数据包,设置 ACK(确认)标志位为 1,确认号为
Ack = x + 1
,序列号为Seq = y
(服务器当前的序列号)。 - 含义: 服务器确认收到客户端的 FIN,告诉客户端“我已知道你要关闭”。但服务器可能仍有数据要发送,因此此时连接尚未完全关闭。
- 状态:
- 客户端收到 ACK 后,从
FIN_WAIT_1
进入FIN_WAIT_2
状态。 - 服务器进入
CLOSE_WAIT
状态。
- 客户端收到 ACK 后,从
第三次挥手(FIN)
- 发送方: 服务器
- 数据包内容: 服务器在完成数据发送后,发送一个 TCP 数据包,设置 FIN 标志位为 1,序列号为
Seq = z
,确认号仍为Ack = x + 1
(可能与第二次挥手相同)。 - 含义: 服务器表示自己也没有更多数据要发送,同意关闭连接。
- 状态: 服务器进入
LAST_ACK
状态。
第四次挥手(ACK)
- 发送方: 客户端
- 数据包内容: 客户端回应一个 TCP 数据包,设置 ACK 标志位为 1,序列号为
Seq = x + 1
,确认号为Ack = z + 1
。 - 含义: 客户端确认收到服务器的 FIN,连接正式关闭。
- 状态:
- 客户端进入
TIME_WAIT
状态,等待 2MSL(两倍的最大段生存时间,通常为 30 秒到 2 分钟),确保网络中残留的数据包消失后,进入CLOSED
状态。 - 服务器收到 ACK 后,直接进入
CLOSED
状态。
- 客户端进入
四次挥手的图示
假设客户端初始序列号为 x
,服务器初始序列号为 y
,以下是过程:
客户端 服务器
| |
| FIN (Seq=x) | ----> 第一次挥手
|----------------->|
| |
| ACK (Seq=y, Ack=x+1) | <---- 第二次挥手
|<-----------------|
| |
| FIN (Seq=z, Ack=x+1) | <---- 第三次挥手
|<-----------------|
| |
| ACK (Seq=x+1, Ack=z+1) | ----> 第四次挥手
|----------------->|
| |
为什么是四次挥手而不是三次?
- TCP 是全双工协议,客户端和服务器都可以独立发送数据。因此,关闭连接时需要双方分别发送 FIN 并得到确认。
- 三次握手是为了建立同步,而四次挥手是因为关闭时服务器可能还有剩余数据要发送,无法将自己的 FIN 和 ACK 合并为一个数据包。
状态变化总结
- 客户端:
ESTABLISHED → FIN_WAIT_1 → FIN_WAIT_2 → TIME_WAIT → CLOSED
- 服务器:
ESTABLISHED → CLOSE_WAIT → LAST_ACK → CLOSED
使用 tcpdump 捕获四次挥手
可以用以下命令捕获四次挥手的数据包:
1 | sudo tcpdump -i en0 -n port 80 or port 443 |
在捕获到的数据中,查找 [FIN]
和 [ACK]
标志的数据包即可看到四次挥手的过程。