引言
TCP(传输控制协议)是网络通信的基石,其核心目标是:
在不可靠的IP层之上提供可靠的数据传输。
为实现这一目标,TCP通过 三次握手(Three-way Handshake)建立连接,通过四次挥手(Four-way Handshake) 终止连接。本期文章我将以通俗易懂的方式解析这两个关键过程的技术细节,并探讨其设计背后的逻辑。
一、三次握手:建立可靠通信信道
1 TCP 报文标志
TCP 协议就像两个人打电话,需要一些“暗号”来协调对话的开始、结束和确认。这些“暗号”就是报文中的标志位,每个标志代表一个特定动作。
1. SYN(Synchronize,同步标志)
- 作用:打招呼,告诉对方“我要和你建立连接”。
- 例子:你打电话给朋友,开口说:“喂,能听到吗?”——这就是
SYN
。- 技术细节:携带初始序列号(类似对话的起始编号),用于三次握手的第一步。
2. ACK(Acknowledgment,确认标志)
- 作用:确认收到对方的消息,回应“我收到了”。
- 例子:朋友回答:“能听到!”——这就是
ACK
。- 技术细节:
ACK
必须配合一个确认号(Acknowledgment Number),表示“我期待你下次从这个编号开始发数据”。
2 握手过程分解
三次握手的目标是同步双方的初始序列号(Sequence Number),确认双方的收发能力正常。具体流程如下:

步骤 | 方向 | 报文标志 | 状态变化 | 核心作用 |
---|---|---|---|---|
第一次握手 | 客户端 → 服务端 | SYN=1,ACK=0,SEQ(顺序号)=x | 客户端进入SYN_SENT | 客户端声明自身具备发送能力 |
第二次握手 | 服务端 → 客户端 | SYN=1, ACK=1, SEQ=y, ACK=x+1 | 服务端进入SYN_RCVD | 服务端确认客户端发送能力,声明自身具备收发能力 |
第三次握手 | 客户端 → 服务端 | ACK=1, ACK=y+1 | 双方进入ESTABLISHED | 客户端确认服务端具备收发能力 |
三次握手的生活例子:打电话确认身份
假设你(客户端)想给朋友(服务端)打电话确认一件事:
第一次握手(SYN):你拨通电话,说:“喂,是老陈吗?”(你发起连接请求)。
第二次握手(SYN-ACK):朋友接听后回答:“是的,我是老陈。你是小梁吗?”(朋友确认你的请求,并反问你的身份)。
第三次握手(ACK):你回答:“对,我是小梁!”(你确认朋友的身份,双方明确彼此身份)。
此时,双方确认了对方的身份和通信能力,可以开始正式对话。
关键点:三次确认确保双方身份和通信能力可靠,避免“假电话”或“听不清”的问题。
如果还听不懂,没关系,下面我再总结一遍三次握手过程:
握手阶段 | 客户端确认内容 | 服务端确认内容 |
---|---|---|
第一次握手 | 仅自身发送能力 | 客户端发送能力 + 自身接收能力 |
第二次握手 | 双方收发能力 | 客户端发送能力 + 自身接收能力 |
第三次握手 | 双方信道就绪 | 自身发送能力 + 客户端接收能力 |
- 第一次握手
- 客户端确认内容:客户端就只看看自己有没有本事把消息发出去,比如检查一下自己的网络设置、相关软件是不是能正常发数据等,只关心自己的发送能力。
- 服务端确认内容:服务端收到客户端发过来的消息后,一方面知道了客户端有发消息的能力,另一方面也看看自己能不能正常接收消息,比如检查自己的接收端口是不是开着,接收数据的程序是不是在正常运行等。
- 第二次握手
- 客户端确认内容:客户端收到服务端的回应后,就知道自己能给服务端发消息,服务端也能收到,同时服务端也能给自己回消息,这就确认了双方的收发能力都是正常的。
- 服务端确认内容:和第一次握手时差不多,还是确认客户端能发消息给自己,同时自己的接收能力也是好的,没有变化。
- 第三次握手
- 客户端确认内容:客户端发送消息给服务端,就是告诉服务端 “我这边一切准备就绪啦,咱们可以开始正式通信啦”,表明双方的通信信道都准备好了,可以正常传输数据了。
- 服务端确认内容:服务端收到客户端的这个消息后,就知道客户端已经准备好接收数据了,同时也再次确认自己给客户端发消息的能力是没问题的,这样双方就可以正式开始进行数据交互了。
关键设计解析
-
为什么需要三次而非两次?
若仅两次握手,服务端无法确认客户端是否收到自己的SYN-ACK
。若客户端未收到此包,服务端会持续等待,导致资源浪费(如半连接队列堆积)。三次握手确保双方均确认对方的收发能力。(全双工通信) -
半连接队列(SYN Queue)与全连接队列(Accept Queue)
- 半连接队列:存储未完成三次握手的连接(
SYN_RCVD
状态),用于防御SYN Flood攻击(通过限制队列大小)。 - 全连接队列:存储已完成握手的连接(
ESTABLISHED
状态),等待应用调用accept()
处理。
- 半连接队列:存储未完成三次握手的连接(
-
第三次握手可携带数据
客户端在第三次握手时即可发送数据(如HTTP请求),而服务端需完成第三次握手后才能发送数据。这一设计优化了通信效率。
二、四次挥手:优雅终止全双工连接
1 挥手过程分解
由于TCP是全双工协议,双方需独立关闭数据通道。流程如下:

步骤 | 方向 | 报文标志 | 状态变化 | 核心作用 |
---|---|---|---|---|
第一次挥手 | 主动关闭方 → 被动关闭方 | FIN=1, SEQ=u | 主动方进入FIN_WAIT_1 | 主动方停止发送数据 |
第二次挥手 | 被动关闭方 → 主动关闭方 | ACK=1, ACK=u+1 | 被动方进入CLOSE_WAIT ,主动方进入FIN_WAIT_2 | 确认收到关闭请求 |
第三次挥手 | 被动关闭方 → 主动关闭方 | FIN=1, SEQ=v | 被动方进入LAST_ACK | 被动方停止发送数据 |
第四次挥手 | 主动关闭方 → 被动关闭方 | ACK=1, ACK=v+1 | 主动方进入TIME_WAIT ,被动方关闭 | 确认最终关闭 |
2 四次挥手的生活例子:礼貌结束对话
假设你和同事在会议室讨论工作,结束后需要离开:
- 第一次挥手(FIN):你说:“我的部分讲完了,我先走了。”(你主动提出结束对话)。
- 第二次挥手(ACK):同事回答:“好的,我知道了。”(同事确认你的结束请求,但可能还有话要说)。
- 第三次挥手(FIN):同事补充:“对了,下周的会议时间别忘了!”(同事完成自己的发言后,也提出结束对话)。
- 第四次挥手(ACK):你回答:“没问题,我会记住的!”(你确认同事的结束请求)。
至此,双方都确认对话结束,各自离开会议室。
关键点:
- 双方需独立确认结束,因为可能各自有未说完的内容(类似TCP的全双工特性)。
- 最后你等待片刻(类似
TIME_WAIT
状态),确保同事没有突然回头补充内容(防止数据丢失)。
关键设计解析
-
为什么需要四次挥手?
被动关闭方可能在收到FIN
后仍有数据待发送(如服务器需发送最后响应),因此需将ACK
与FIN
分开发送。 -
TIME_WAIT状态的意义
- 确保最后一个
ACK
到达:若该ACK
丢失,被动方会重传FIN
,主动方可再次响应。 - 防止旧连接数据干扰:等待2MSL(报文最大生存时间,通常60秒)确保网络中残留的旧报文失效。
- 确保最后一个
-
CLOSE_WAIT状态积压问题
若程序未正确调用close()
,会导致大量连接滞留CLOSE_WAIT
状态,引发资源泄漏。需通过代码审查或监控工具排查。
三、总结
通过生活化的例子,可以直观理解:
- 三次握手是“建立信任”的过程,确保双方能正常通信。
- 四次挥手是“礼貌告别”的过程,确保双方数据完整传输后终止连接。
这种机制保障了TCP协议在不可靠的网络环境中实现可靠传输。
码字不易,希望可以一键三连,我们下期文章再见!!!