深入理解 TCP 协议:核心特性与机制详解
TCP(Transmission Control Protocol,传输控制协议)是计算机网络中最核心、最复杂的协议之一。与 UDP 的简单粗暴不同,TCP 提供了面向连接、可靠、基于字节流的传输服务。
本文将详细剖析 TCP 的报文结构、核心特性(连接管理、可靠传输、流量控制、拥塞控制),以及开发中常见的粘包问题和状态分析。
一、TCP 报文段结构
TCP 报文段由 TCP 首部 和 TCP 数据 两部分组成。首部的前 20 个字节是固定的,后面可能有可变长度的选项。
text
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 源端口号 (16) | 目的端口号 (16) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 序号 Sequence Number (32) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 确认号 Acknowledgment Number (32) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 数据偏移(4) | 保留(6) |U|A|P|R|S|F| 窗口大小 (16) |
| | |R|C|S|S|Y|I| |
| | |G|K|H|T|N|N| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 校验和 (16) | 紧急指针 (16) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 选项 (长度可变) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 数据 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1. 核心字段解析
- 源端口 / 目的端口:标识发送端和接收端的应用进程。
- 序号(Sequence Number) :TCP 是面向字节流的,序号表示本报文段所发送数据的第一个字节的序号。
- 确认号(Acknowledgment Number):期望收到对方下一个报文段的第一个数据字节的序号。表示该序号之前的所有数据都已经正确接收。
- 数据偏移:指出 TCP 首部的长度(因为可能有选项),单位是 32 位字(4 字节)。最小值为 5(即 20 字节)。
- 标志位(控制位) :
URG:紧急指针有效。ACK:确认号有效。连接建立后通常都为 1。PSH:接收方应尽快将这个报文交给应用层,而不是等缓冲区满。RST:重置连接(强制断开)。SYN:同步序号,用于建立连接(三次握手)。FIN:发送端数据发送完毕,请求释放连接(四次挥手)。
- 窗口(Window) :接收方告诉发送方自己还能接收多少字节的数据,用于流量控制。
- 校验和:校验首部和数据,计算时同样需要加上伪首部。
二、TCP 的核心特性
1. 面向连接(三次握手与四次挥手)
TCP 通信前必须先建立连接,通信结束后必须释放连接。
(1)三次握手(建立连接)
text
Client Server
| |
| ------------ SYN (seq=x) ---------->| (1) 客户端请求建立连接
| |
| <--- SYN+ACK (seq=y, ack=x+1) -------| (2) 服务端同意并回应
| |
| ------------ ACK (ack=y+1) --------->| (3) 客户端确认
| |
| <============ 连接建立 =============>|
为什么是三次?
为了确认双方的发送能力 和接收能力都正常。
- 第一次:服务端确认了客户端的发送能力。
- 第二次:客户端确认了服务端的接收和发送能力。
- 第三次:服务端确认了客户端的接收能力。
如果只有两次,服务端无法确认客户端是否收到了自己的 SYN+ACK,且可能因为网络延迟收到过期的历史连接请求,导致资源浪费。
(2)四次挥手(释放连接)
text
Client Server
| |
| ------------ FIN (seq=u) ----------->| (1) 客户端数据发完了
| |
| <----------- ACK (ack=u+1) ----------| (2) 服务端确认
| |
| (服务端可能还有数据要发) |
| |
| <----------- FIN (seq=w) ------------| (3) 服务端数据也发完了
| |
| ------------ ACK (ack=w+1) --------->| (4) 客户端确认
| |
| (等待 2MSL 后彻底关闭) |
为什么是四次?
TCP 是全双工的,连接释放需要双方都发送 FIN。
当客户端发送 FIN 后,只表示客户端没有数据要发了,但依然可以接收数据。服务端收到 FIN 后,先回 ACK,然后处理完剩余数据,再发送自己的 FIN。因此 ACK 和 FIN 不能合并,必须分开发送。
2. 可靠传输(机制组合拳)
网络环境是不可靠的,TCP 通过以下机制保证数据不丢失、不重复、按序到达:
- 校验和:发现数据在传输过程中的比特错误,错误则丢弃。
- 序列号与确认应答(ACK):发送方给每个字节编号,接收方收到后回复 ACK。发送方根据 ACK 确认数据被接收。
- 超时重传:发送方发出数据后启动定时器,如果超时未收到 ACK,则重新发送该数据。
- 连接管理:通过三次握手和四次挥手保证连接的可靠建立和释放。
- 丢弃重复数据:接收方通过序列号去重。
3. 流量控制(滑动窗口)
目的:防止发送方发送速度过快,导致接收方缓冲区被打满而丢包。
- 滑动窗口机制 :接收方在 ACK 报文中的
Window字段告诉发送方自己当前的接收窗口大小(即还能接收多少数据)。 - 零窗口 :如果接收方缓冲区满了,会发送窗口大小为 0 的报文。此时发送方停止发送,并启动持续计时器,定时发送零窗口探测报文,询问接收方窗口是否变大。
4. 拥塞控制
目的:防止过多的数据注入到网络中,导致路由器或链路过载(全局性问题)。
TCP 采用四种算法协同工作:
- 慢启动(Slow Start) :
- 刚建立连接时,拥塞窗口(cwnd)设为 1 个 MSS(最大报文段长度)。
- 每收到一个 ACK,cwnd 翻倍(指数增长)。
- 达到慢启动阈值(ssthresh)后,进入拥塞避免。
- 拥塞避免(Congestion Avoidance) :
- 每个 RTT(往返时间),cwnd 加 1(线性增长),让网络慢慢试探上限。
- 快重传(Fast Retransmit) :
- 如果发送方连续收到 3 个重复的 ACK(说明某个包丢了,但后续包收到了),立即重传丢失的报文,而不必等超时定时器到期。
- 快恢复(Fast Recovery) :
- 执行快重传后,将 ssthresh 和 cwnd 减半,然后直接进入拥塞避免状态,而不是回到慢启动。
5. 面向字节流与全双工
- 全双工:TCP 允许通信双方在同一时刻互相发送数据。两端都有发送缓冲区和接收缓冲区。
- 面向字节流:TCP 把应用层交下来的数据看成是一连串的无结构字节流。它不保留应用层消息的边界。
三、TCP 的常见问题与注意点
1. 粘包与拆包
由于 TCP 是面向字节流 的,它没有消息边界。如果发送方连续发送两条消息 "Hello" 和 "World",接收方可能会一次读取到 "HelloWorld",这就是粘包 ;也可能一次只读到了 "Hel",这就是拆包。
产生原因:
- 发送方使用了 Nagle 算法(合并小包发送)。
- 接收方应用层读取不及时,缓冲区积累了多个包。
- 数据包大于 MSS,发生 IP 分片。
解决方案(应用层协议设计):
- 定长消息:规定每个消息固定长度,不够补齐(浪费带宽)。
- 特殊分隔符 :如使用
\r\n作为消息结束标志(如 HTTP 协议)。 - 长度前缀:在消息头部用固定几个字节标明消息体长度(最常用,如 Protobuf、Dubbo)。
2. 常见 TCP 状态解析
LISTEN:服务端套接字处于监听状态,等待客户端连接。ESTABLISHED:连接已建立,正常通信状态。CLOSE_WAIT:被动关闭方收到对方的 FIN,但自己还没调用close()。如果大量出现,说明代码有 Bug(忘记关闭连接)。TIME_WAIT:主动关闭方最后进入的状态,等待 2MSL(报文最大生存时间的两倍)后彻底关闭。- 作用 1:确保最后一个 ACK 能被对方收到。如果 ACK 丢失,对方会重发 FIN,此时处于 TIME_WAIT 的一方可以再次回复 ACK。
- 作用 2:防止历史连接的数据包干扰新建立的连接。
3. 长连接与短连接
- 短连接:每次请求都经历"建立连接 -> 传输数据 -> 断开连接"。简单,但三次握手和四次挥手开销大。
- 长连接:建立连接后,保持连接不断开,多次请求复用同一个 TCP 连接。性能高,但需要心跳机制(KeepAlive)维持连接,防止长时间无数据被防火墙掐断。
四、总结
TCP 协议通过一系列精妙的机制,在不可靠的 IP 网络层之上构建了可靠的传输通道:
- 三次握手、四次挥手实现了安全的连接管理。
- 序列号、确认应答、超时重传保证了可靠性。
- 滑动窗口实现了流量控制,避免压垮接收方。
- 慢启动、拥塞避免实现了拥塞控制,避免压垮网络。
- 面向字节流的特性要求我们在应用层处理好粘包/拆包问题。
深刻理解 TCP 的这些特性,不仅有助于应对面试,更是编写高性能、高可靠网络程序(如 RPC 框架、网关、消息队列)的基石。