PASSWORD
SHOW
SHOW
TYPE
Post
STATUS
Published
SLUG
/tcp
SUMMARY
TAGS
学习
记录
ICON
CODEURL
DATE
Jul 23, 2026
PAGE
MODULE
计算机网络
TCP三次握手和四次挥手简单理解记录
TCP 连接
一、 基础概念与核心机制
基础认知:
- 全双工 (Full-Duplex):TCP 连接允许双方在任何时候独立发送和接收数据。这意味着连接的建立是双向的,连接的释放也必须是双向的。
- 序列号 (Sequence Number):TCP 是面向字节流的协议。每个字节都有编号(初始序列号 ISN + 偏移量)。序列号用于保证数据按序到达、去重以及实现可靠传输。
- 标志位 (Flags):
SYN(Synchronize):同步序列号,用于建立连接。ACK(Acknowledgment):确认收到数据。FIN(Finish):发送方数据发送完毕,请求释放连接。RST(Reset):强制重置连接,通常表示异常。
- 初始序列号 (ISN) 的生成机制:
- 误区:ISN 并非固定为 0,也不是纯随机数。
- 原理:现代操作系统通常基于时钟(如每 4 微秒加 1)生成 ISN。
- 核心原因:防止历史报文被错误接收(如果每次从 0 开始,旧连接的延迟报文可能被新连接误认为有效);同时增加安全性,防止黑客猜测序列号进行 TCP 劫持。
二、 TCP 连接建立:三次握手 (Three-Way Handshake)
核心目的:同步双方的初始序列号 (ISN),确认双方的收发能力都正常。
1. 场景模拟 (Alice 打电话给 Bob)
- Alice: “Bob 你可以听到我说话吗?我是 Alice。” (SYN)
- Bob: “可以听到。Alice 你听得到我说话吗?我是 Bob。” (SYN + ACK)
- Alice: “可以听到,那我们开始聊正事。” (ACK)
2. 详细流程与状态变迁
假设 Client(简写为 C)的初始序列号为 x,Server(简写为 S) 的初始序列号为 y。
步骤 | 方向 | 标志位 / 内容 | 状态变迁 | 说明 |
1 | C -> S | SYN=1, Seq=x | C: SYN-SENT | 客户端请求建立连接。标准 SYN 报文不能携带数据,但消耗一个序号。 |
2 | S -> C | SYN=1, ACK=1, Seq=y, ack=x+1 | S: SYN-RCVD | 服务器确认收到 SYN (ack=x+1),同时发起自己的连接请求 (Seq=y)。 |
3 | C -> S | ACK=1, Seq=x+1, ack=y+1 | C: ESTABLISHED
S: ESTABLISHED | 客户端确认收到服务器的 SYN。ACK 报文如果没数据,不消耗序号。 |
3. 常见问题
- Q1: 为什么要三次握手,两次不行吗?
- 防坑答案:主要不是为了确认“双方都能收发”,而是为了防止失效的连接请求突然传到了服务端。
- 场景:Alice 发出的第一个 SYN 堵在网络里了,Alice 以为丢了重发了一个。连接建立并结束后,那个堵住的旧 SYN 才到达 Bob。
- 如果是两次握手:Bob 收到旧 SYN 立刻建立连接并分配资源,等于白白浪费资源等待 Alice 发数据。
- 如果是三次握手:Bob 回复 SYN+ACK,Alice 收到后发现“我没想连啊”,发
RST拒绝,Bob 释放资源。
- Q2: 第三次握手失败了怎么办?
- 服务端发了 SYN+ACK 后如果没有收到客户端的 ACK,会重传 SYN+ACK (指数退避)。如果多次重传仍无反应,服务端会强制关闭连接。
- Q3: SYN Flood 攻击与防御?
- 攻击原理:攻击者伪造大量虚假 IP 发送 SYN。服务器回复 SYN+ACK 后死等第三次 ACK,导致半连接队列 (SYN Queue) 被占满,耗尽服务器资源。
- 防御手段:开启
SYN Cookies。服务器收到 SYN 后不立即分配资源,而是根据源 IP、端口等计算出一个 Cookie 作为序列号发回去。只有收到合法 ACK 才分配资源。
- Q4: SYN 报文绝对不能带数据吗? (TCP Fast Open)
- 在标准 TCP 中,SYN 不能携带应用层数据。
- 但在 TFO (TCP Fast Open) 扩展中,客户端可以在 SYN 报文中携带数据(通过首次连接颁发的 Cookie 机制),从而减少一次 RTT(往返时间),常用于 HTTP 请求优化。
三、 TCP 连接释放:四次挥手 (Four-Way Wave)
核心目的:TCP 是全双工的,关闭必须是双向的。我不发了,但我还可以收。
1. 场景模拟 (Alice 和 Bob 挂电话)
- Alice: “我的事说完了,我不说了,你继续说。” (FIN) —— Alice 进入半关闭状态
- Bob: “好的,我知道你的事说完了,那我接着说。” (ACK) —— Bob 还能继续说 (此时可能有数据在传输,Bob 继续告诉 Alice 一些事情)
- Bob: “我也说完了,挂了吧。” (FIN)
- Alice: “好,再见。” (ACK) (进入 TIME-WAIT,等待 2MSL 后关闭)
2. 详细流程与状态变迁
假设 Client(简写为 C) 结束时的序列号为 u,Server(简写为 S) 结束时的序列号为 v。
步骤 | 方向 | 标志位 / 内容 | 状态变迁 | 说明 |
1 | C -> S | FIN=1, Seq=u | C: FIN-WAIT-1 | 客户端请求关闭发送通道。FIN 报文即使没数据也消耗一个序号。 |
2 | S -> C | ACK=1, Seq=v, ack=u+1 | S: CLOSE-WAIT
C: FIN-WAIT-2 | 服务器确认。此时处于半关闭状态:C 不能发数据,但 S 还能发,C 照样收。 |
(数据) | S -> C | ... | ... | (Server 将剩余数据发送完毕) |
3 | S -> C | FIN=1, ACK=1, Seq=w, ack=u+1 | S: LAST-ACK | 服务器数据发完了,请求关闭它的发送通道。 |
4 | C -> S | ACK=1, Seq=u+1, ack=w+1 | C: TIME-WAIT
S: CLOSED | 客户端确认。注意:Client 进入 TIME-WAIT,并没有直接关闭! |
3. 常见问题
- Q1: 为什么建立是三次,释放却要四次?
- 核心原因:建立连接时,Server 的应答 (ACK) 和同步请求 (SYN) 可以合并在同一个包 (SYN+ACK) 里发送。
- 但在释放时,Client 发 FIN 只是说“我发完了”。Server 收到后先回 ACK(防超时重传),但 Server 可能还有数据没传完,不能立刻发 FIN。所以 ACK 和 FIN 分家了。
- Q2: TIME-WAIT 状态为什么要等 2MSL?
- 定义:主动发起关闭的一方,发送完最后一个 ACK 后,必须等待 2MSL (Maximum Segment Lifetime,最长报文段寿命,Linux 通常默认 60 秒) 才能进入 CLOSED 状态。
- 原因 1 (保底):防止最后一个 ACK 丢了。如果 Server 没收到 ACK,会重传 FIN。Client 如果早早跑了,Server 就永远收不到回复了。Client 必须等着扫尾。
- 原因 2 (清场):防止“已失效的连接请求报文段”出现在下一个新连接中。等待 2MSL 可以让本连接产生的所有报文都在网络中消逝(1 MSL 去,1 MSL 回)。
- 为什么是 2MSL 而不是 1MSL?:刚好覆盖了“ACK 丢失导致对端重传 FIN”的一个完整来回时间。如果只有 1MSL,ACK 丢了,对端重传 FIN 时,客户端已关闭,会回复 RST,导致服务端异常。
- Q3: CLOSE-WAIT 状态过多说明什么?
- 说明服务器端的代码可能有 Bug。收到了客户端的 FIN,回复了 ACK,但迟迟没有发起自己的 FIN(比如忘记在代码中调用
close()函数),导致连接一直卡在半关闭状态,占用文件描述符和内存。
四、 异常场景与边界情况
- 同时打开 (Simultaneous Open)
- 双方几乎同时发送 SYN。状态变为:
SYN-SENT->SYN-RCVD->ESTABLISHED。 - 依然需要交换四次报文(因为无法合并),但逻辑上依然称为“三次握手”的变体。
- 同时关闭 (Simultaneous Close)
- 双方几乎同时发送 FIN。状态变为:
FIN-WAIT-1->CLOSING->TIME-WAIT。 - 双方都需要等待 2MSL 才能关闭。
- RST (Reset) 报文的作用
- 当收到不期望的报文(如未监听的端口收到 SYN,或已关闭的连接收到数据)时,会发送 RST 强制断开连接,不经过四次挥手。
- 这也是排查
Connection reset by peer错误的根源。
- TCP 保活机制 (Keepalive)
- 在 CLOSE-WAIT 过多的问题中,提到了占用资源。
- 补充 TCP Keepalive 机制(心跳包)。服务器可以通过
SO_KEEPALIVE选项,定期探测死连接,从而清理掉那些因为客户端崩溃而卡在 CLOSE-WAIT 的连接。
五、 对比总结
1. 极简对比总结表
特性 | 三次握手 (建立) | 四次挥手 (释放) |
发起方 | 通常称 Client | 双方都可以发起,通常是 Client |
关键标志 | SYN -> SYN+ACK -> ACK | FIN -> ACK -> (数据) -> FIN -> ACK |
消耗序号? | SYN 消耗 1 | FIN 消耗 1 |
中间状态 | SYN-SENT, SYN-RCVD | FIN-WAIT, CLOSE-WAIT, TIME-WAIT |
等待 | 无 | TIME-WAIT (2MSL) |
核心逻辑 | 1. 我想连
2. 确认你想连,我也想连
3. 好的,确认 | 1. 我不发了
2. 知道了 (还能发)
3. 我也不发了
4. 拜拜 |
2. 常见考法
- ACK 确认号怎么算?
- 永远是“对方发来的序号 + 数据长度”。
- 如果对方发的是 SYN 或 FIN (占 1 个序号),就是
对方 Seq + 1。
- 谁进入 TIME-WAIT?
- 主动发起关闭的一方。如果服务器主动踢人,服务器会进入 TIME-WAIT。
- 记忆口诀
- 三次握手:
SYN, SYN+ACK, ACK(请求,确认+请求,确认) - 四次挥手:
FIN, ACK, FIN, ACK(分手,知道,我也分,拜拜) - TIME-WAIT 等待 2MSL:
等最后 ACK 不丢,等旧包全部死绝
有关Notion安装或者使用上的问题,欢迎您在底部评论区留言,一起交流~
- Author:Sunrise.xiao
- URL:http://preview.tangly1024.com/article/tcp
- Copyright:All articles in this blog, except for special statements, adopt BY-NC-SA agreement. Please indicate the source!






