目录
TCP连接
TCP位于OSI七层网络模型中的第四层传输层,它的核心任务只有一个:在不可靠的IP网络层之上,构造一个可靠的 ,面向连接的 ,字节流的连接通道。
这里有三个关键词:
- 靠的:保证传输的信息报文不丢,不乱,不错。
- 面向连接的:传递信息报文之前要建立连接,保证连接通道可正常传输信息。这就是本章重点:三次握手&四次挥手
- 字节流的:TCP把数据看作一连串无结构的字节流。
要了解三次握手和四次挥手的过程,首先要了解TCP报文的组成。TCP报文由头部 和数据两部分组成,我们主要看头部,它包含了TCP工作的所有控制信息。

上图就是TCP报文段的头部,通常是20个字节,我们主要关注五个核心字段
- 源端口号&目的端口号(各占两字节):IP地址解决了信息发送到哪台电脑的问题,而端口号解决了数据发送到的电脑上哪个进程的问题。
- 序列号Seq(四字节):本报文段所发送的数据的第一个字节的序号,解决网络包乱序的问题。
- 确认号ack(四字节):表示期望收到对方下一个报文段的第一个数据字节的序列号。
- 标志位 :有几个重要的标志位
- SYN:同步位。SYN=1 表示这是连接同步报文,用于初始化序列号;仅在三次握手的前 2 个报文中置 1;建立连接。简单点说就是表明这次通信适用于建立连接的。不是用于传输数据的。SYN=1,此次传输不能携带数据。
- ACK:确认位。ACK=1 表示确认号 ack 字段有效;TCP 规范强制规定:连接建立完成后,所有传输的报文段必须将 ACK 置 1。
- FIN:关闭连接
- RST:重置异常链接
- 窗口大小:接收缓冲区还能容纳多少字节数据。这是实现流量控制的基础。
!!!注意,大写的ACK和小写的ack不是一个东西!!!大写的ACK是一个标识,只有1bit,ACK=1 表示确认号 ack 字段有效,而小写的ack是序列号,有32bit。
也可以简单理解成ACK=1时,ack才会启用,不然是无效的。
三次握手(建立连接)

第一次握手
客户端向服务端发起连接请求。同步自己的初始序列号。
状态变化:客户端从 CLOSED → SYN_SENT(同步已发送)
报文头部字段变化:
- SYN = 1(表明这是一次连接同步报文)
- Seq = x(随机生成)
- ACK = 0
- ack = 0(ACK=0,ack无效,默认为0)
关键说明 :该报文仅用于同步序列号,不能携带应用数据;因 SYN=1,该报文固定消耗 1 个序列号,后续服务端的 ack 必须为 x+1。
第二次握手
服务端收到客户端的 SYN 请求后,同时完成两件事:① 确认客户端的同步请求;② 向客户端同步自己的初始序列号。
状态变化:服务端从 LISTEN → SYN_RCVD(同步已收到)
报文头部字段变化:
- SYN = 1(表明这是一次连接同步报文)
- Seq = y(随机生成)
- ACK = 1(开启确认功能,ack 字段生效)
- ack = x + 1(上一次握手客户端传过来Seq = x + SYN消耗的一个字节。代表服务端已完整收到客户端 Seq = x 的报文,期望下一次收到客户端的 Seq 从 x+1开始)
关键说明 :这是三次握手中唯一同时将 SYN 和 ACK 置 1 的报文,不能携带应用数据 ;因 SYN=1,该报文固定消耗 1 个序列号,后续客户端的 ack 必须为 y+1。
第三次握手
客户端收到服务端的 SYN+ACK 后,向服务端发送最终确认报文,完成连接建立。
状态变化:客户端从 SYN_SENT → ESTABLISHED(连接已建立);服务端收到该报文后,从 SYN_RCVD → ESTABLISHED(连接已建立),全双工连接正式建立。
报文头部字段变化:
- SYN = 0(初始序列号同步完成,无需再置位)
- Seq = x + 1(随机生成)
- ACK = 1(开启确认功能,ack 字段生效)
- ack = y + 1(上一次握手客户端传过来Seq = y + SYN消耗的一个字节。代表客户端已完整收到服务端 Seq=y的报文,期望下一次收到服务端的 Seq 从 y+1开始)
关键说明 :该报文可以携带应用数据:如果不携带数据,不消耗序列号;如果携带数据,消耗对应数据长度的序列号。
常见问题
为什么是三次握手,而不是二次或四次。
答:核心是为了避免失效的连接请求报文传到服务端,建立无效的连接,浪费系统资源。同时,只有三次握手客户端和服务端才能确认彼此都有接收和发送的能力。如果没有第三次握手,服务端无法确认客户端的接收能力。
- 第一次客户端向服务端发送SYN报文,服务端接收报文。此时服务端确认客户端有发送能力。
- 第二次服务端向客户端发送SYN报文,客户端接收报文。此时客户端确认服务端有接收和发送能力。
- 第三次客户端向服务端发送报文,服务端接收报文。此时服务端确认客户端有接收能力。
四次挥手(断开连接)
客户端与服务端建立的TCP连接是**全双工通信协议,**也就是说要想断开一个TCP连接,就必须双方都确认关闭自己的发送通道,这就是 "四次挥手" 的根本原因。而断开连接的发起者,可以是客户端,也可以是服务端。(下面我们以客户端为发起者演示)

第一次挥手
客户端向服务端发送FIN报文,序号为**seq=u**(u 是客户端当前已发送数据的最后一个字节序号 + 1)
客户端状态 :从ESTABLISHED 变为FIN_WAIT_1
含义:"我客户端没有数据要发给你了,我要关闭我到你的发送通道"
第二次挥手
服务器 收到 FIN 后,立即发送一个ACK=1 的报文段,确认号为ack=u+1,序号为**seq=y**(y 是服务器当前已发送数据的最后一个字节序号 + 1)
服务器状态 :从ESTABLISHED 变为CLOSE_WAIT
客户端状态 :收到 ACK 后,从FIN_WAIT_1 变为FIN_WAIT_2
含义:"我服务器收到了你关闭发送通道的请求,我确认了"
第二次挥手结束后,客户端已经关闭发送数据报文的通道了。但是还可以发送控制报文(ACK、FIN、RST、SYN),来辅助服务端关闭连接通道。
第三次挥手
当服务器也没有数据要发送给客户端时,调用close()函数,发送一个FIN=1,ACK=1 的报文段,确认号仍然是**ack=u+1,序号为seq=w**(w 是服务器在 CLOSE_WAIT 状态下发送的最后一个字节序号 + 1)
服务器状态 :从CLOSE_WAIT 变为LAST_ACK
含义:"我服务器也没有数据要发给你了,我也要关闭我到你的发送通道"
第四次挥手
客户端操作 :收到 FIN 后,立即发送一个ACK=1 的报文段,确认号为ack=w+1,序号为seq=w+1
客户端状态 :从FIN_WAIT_2 变为TIME_WAIT
服务器状态 :收到 ACK 后,从LAST_ACK 变为CLOSED,连接完全关闭
客户端后续 :在TIME_WAIT 状态等待2MSL (最长报文段寿命)时间后,也变为CLOSED状态
含义:"我客户端收到了你关闭发送通道的请求,我确认了"
至此,TCP连接正式断开。