
TCP 是互联网基石,HTTP、HTTPS、MySQL、Redis、SSH 全部基于 TCP。很多开发者只会背“三次握手、四次挥手”,但不懂为什么这么设计、序号怎么涨、窗口干嘛用、标志位区别、流量与拥塞控制的差异。
本文是一套从零到精通的 TCP 完整体系,内容理解来自网络及个人学习
一、TCP 是什么?核心特性总览
TCP(Transmission Control Protocol,传输控制协议)是面向连接、可靠、全双工、基于字节流的传输层协议。
四大核心能力,也是 TCP 区别于 UDP 的本质:
- 面向连接:传输前必须握手建立连接,传输后挥手释放连接
- 可靠传输:保证数据不丢、不重、不乱
- 流量控制:解决「发送太快、接收处理不过来」的问题
- 拥塞控制:解决「网络链路拥堵、丢包超时」的问题
UDP 是“只管发、不管到”,TCP 是“发一步、确认一步、全程可控”。
二、TCP 报文头核心字段

TCP 所有的握手、挥手、可靠传输、控速逻辑,全部来自报文头字段,只需掌握7个核心要素:
1. SEQ 序列号(32位)
作用:给每一个传输字节编号
核心价值:排序、去重、重传定位。
规则:
- 连接建立时生成 ISN 初始序列号(基于时钟、每4ms+1,防旧报文干扰)
- 数据传输阶段:SEQ 只按字节累加,和时间无关
- 未收到 ACK 则一直重传相同 SEQ,不更新序号
- SYN、FIN 各占用 1 个序列号(无数据但占号)
2. ACK 确认号(32位)
作用:告诉对方“我收到哪了”
公式恒成立:ACK = 对方上一次 SEQ + 已接收字节数
只要收到合法 ACK,代表对应数据彻底确认成功,发送方可以滑动窗口、发送新数据。
彼此后续开始传输数据时候的七点即为发送时生成的ISN+1
3. Window 窗口(16位)
作用:流量控制核心
含义:当前主机的接收缓冲区剩余容量,单位字节。用于告诉发送方:你最多还能发多少数据。
逻辑:
- 应用读得快 → 缓冲区空 → 窗口变大 → 发送提速
- 应用读得慢 → 缓冲区满 → 窗口变小 → 发送降速
- 窗口=0 → 暂停发送普通数据
通过窗口的大小来实时调试传输速率,控制流量。
4. 六大标志位
- SYN:建立连接
- FIN:关闭连接
- ACK:确认报文(几乎所有传输报文都带ACK)
- RST:强制重置连接
- PSH:推送位,立即交付数据
- URG:紧急位,带外插队数据
SYN:
SYN指的在连接建立时用来同步序号。当SYN=1而ACK=0时,表明这是一个连接请求报文。对方若同意建立连接,则应在响应报文中使SYN=1和ACK=1。 综合一下,SYN置1就表示这是一个连接请求或连接接受报文。
ACK:
TCP实现可靠传输的核心机制之一,贯穿连接建立、数据传输和连接释放的全过程,确保数据按序、无丢失地送达。若ACK=0,则报文不携带确认信息,当ACK=1时,即带有一个对已被成功接收报文段的确认序号,这个序列号也是下次接收时期望的数据开始位置。
FIN:
用于在释放TCP连接时,标识发送方比特流结束,用来释放一个连接。当 FIN = 1时,表明此报文的发送方的数据已经发送完毕,并要求释放连接。
PSH :
TCP 默认会「攒包合并、超时交付」提升效率。即数据会在缓存区等待上传,一般是是合并数据后一并上传或是根据设计好的超时时间,时间到了即上传。
PSH=1 表示:不要攒包、不要等待,把缓冲区所有有序数据立刻交给应用层。即在收到psh=1的报文时,将缓存去现有的有序的数据和报文带有的数据直接上传提交给应用层。
受窗口限制、有序、普通数据通道。
URG :
URG=1 配合紧急指针,标记紧急带外数据。
特点:窗口为0也能发、优先处理、不走普通缓冲区队列、应用需单独读取,用于中断、紧急指令。直接根据紧急指针所指将数据提交给应用层。
5. MSS 最大报文段长度
握手阶段协商,告知对方自己单次能接收的最大数据长度,避免分片,提升传输效率。
三、三次握手:为什么三次?完整流程+序号规则
目的:双向确认收发能力正常(客户端能发能收、服务端能发能收)。
TCP 是全双工,需要确认两个方向通道都可用。
1. 第一次握手(C→S)
客户端发送 SYN=1,SEQ=C_ISN
状态:SYN_SENT
含义:客户端请求建立连接,同步初始序号。
2. 第二次握手(S→C)
服务端返回 SYN=1,ACK=1,SEQ=S_ISN,ACK=C_ISN+1
状态:SYN_RCVD
含义:服务端收到客户端请求,同时同步自己的初始序号。
3. 第三次握手(C→S)
客户端发送 ACK=1,ACK=S_ISN+1
双方状态变为:ESTABLISHED(连接建立)
关键结论
- SYN 占用序列号,所以 ACK 必须 +1
- 业务数据起点:客户端从 C_ISN+1 开始,服务端从 S_ISN+1 开始
- 为什么不是两次?两次只能证明「客户端能发、服务端能收」,无法验证「服务端能发、客户端能收」
- 为什么不是四次?SYN+ACK 可以合并,无需多余一次
四、四次挥手:为什么四次?半关闭原理+TIME_WAIT
TCP 全双工通道,读写通道独立关闭,因此无法像握手一样合并,必须四次。
核心规则:FIN 同样占用一个序列号。
1. 第一次挥手(主动端→被动端)
主动端发送 FIN=1,SEQ=x,状态:FIN_WAIT_1
含义:我不再发新数据,请求关闭发送通道。
2. 第二次挥手(被动端→主动端)
被动端回复 ACK=1,ACK=x+1,状态:CLOSE_WAIT
主动端进入 FIN_WAIT_2
此时进入半关闭状态:主动端只收不发,被动端可继续发剩余数据。
3. 第三次挥手(被动端→主动端)
被动端数据发完,发送 FIN=1,SEQ=y,状态:LAST_ACK
含义:我也没数据了,彻底关闭通道。
4. 第四次挥手(主动端→被动端)
主动端回复 ACK=1,ACK=y+1
被动端收到立刻变为 CLOSED。
主动端进入 TIME_WAIT(2MSL),超时后彻底关闭。
TIME_WAIT 两大核心作用
- 保证最后一次 ACK 可靠到达,避免对方重传 FIN
- 等待网络残留旧报文彻底失效,防止新连接被旧数据干扰
五、流量控制 vs 拥塞控制
1. 流量控制(Flow Control)
针对端与端:解决「接收方应用读得慢、缓冲区满」的问题。
依靠 Window 接收窗口 实现,由接收方主动告知发送方限速。
2. 拥塞控制(Congestion Control)
针对网络链路:解决「中间路由拥堵、链路丢包」的问题。
依靠 拥塞窗口 cwnd 实现,发送方自己探测网络状态、主动降速。
3. 发送速率最终公式
可发送数据量 = min(接收窗口rwnd, 拥塞窗口cwnd)
四大拥塞算法阶段
- 慢启动:指数增长,快速探测网络容量
- 拥塞避免:线性增长,平稳传输
- 快重传:连续3次冗余ACK,立刻重传,不等待超时
- 快恢复:丢包后不回到初始最小值,降低减速幅度
六、TCP 粘包与拆包
TCP 是字节流协议,无报文边界,操作系统会自动合并、拆分数据,导致粘包拆包。
产生原因
- 发送方缓冲区攒包合并(Nagle 算法)
- 接收方缓冲区多次数据合并读取
- 大数据被 MSS 拆分传输
解决方案
- 固定报文长度
- 报文首尾分隔符
- 长度头+数据体(工业最常用)
七、TCP 核心面试问答
1. 为什么三次握手、四次挥手?
握手:SYN+ACK 可合并,双向确认收发正常;挥手:读写通道独立关闭,中间可能残留数据,无法合并。
2. SEQ 什么时候按时间、什么时候按字节?
仅 ISN 初始序号基于4ms时钟自增;连接建立后全部按字节累加,重传序号不变。
3. PSH 和 URG 区别?
PSH:普通数据、立即交付、受窗口限制;URG:紧急带外数据、窗口为0也可发送、优先插队处理。
4. 流量控制和拥塞控制区别?
流量控制管「接收方能不能扛住」,拥塞控制管「网络链路能不能扛住」。
5. 为什么需要 TIME_WAIT?
保证最后ACK可靠送达、清空链路残留旧报文,避免新连接异常。
6.tcp为什么可靠?
有以下原因:
- ack确认数据完整度:在接收数据时,每一个字节都有自己的序列号,发送方发送后如果接收不到ack就会一直重复发送,保证数据的顺序和完整性
- 校验和:接收方收到后会计算校验和,校验失败直接丢弃,等待重传,
- mss机制:在请求建立的时候可以添加的每次上传最大的数据量,只出现在SYN中,越大网络的利用率越高
- 重传机制:当发送方发送了相关数据但没有接收到对应的ack时,等待一定时间会重传数据;且若同时收到三个相同的ack时说明传输失败,会无视RTO直接快速重传(速度更快)
- SACK(选择性确认):在选项字段告诉发送方那些数据位置丢失,发送方只用发送缺失的,不用全部重新发送
- D-SACK(重复sack):告诉发送方哪些数据重复发送了,用于判断是否失序丢失等
- 三次握手建立可靠和四次挥手确保连接断开有序
八、全文总结
TCP 的所有设计逻辑,归根到底三件事:
- 用 SEQ/ACK 保证可靠有序
- 用窗口保证端侧收发匹配
- 用拥塞算法保证网络链路稳定
三次握手建通道、四次挥手拆通道、标志位处理特殊场景、双窗口控制传输速度,这就是 TCP 完整的核心体系。


