TCP 协议详解:可靠传输的基石
TCP(Transmission Control Protocol)是互联网最核心的协议之一。它不像 HTTP 那样直接面向用户,但每一页网页、每一条消息、每一次视频通话,都依赖它在幕后确保数据准确、有序地送达。
一、TCP 是什么
TCP 全称 传输控制协议,工作在网络模型的 传输层。它的职责很简单:让两台设备之间的数据传输变得 可靠。
核心能力
- 可靠传输:数据丢包会自动重传,确保对方收到
- 按序交付:即使网络路径不同,数据也能按发送顺序重组
- 流量控制:发送方不会 overwhelm 接收方
- 拥塞控制:网络拥堵时自动降低发送速率
- 全双工通信:双方可以同时发送和接收数据
二、TCP 的核心机制
1. 三次握手(建立连接)
TCP 是面向连接的协议,通信前必须先"握手"确认双方状态:
客户端 服务器
| |
| ---- SYN=1, seq=x ---> | (我想连你,我的序号是 x)
| |
| <- SYN=1, ACK=1, | (好的,我收到了,我的序号是 y)
| seq=y, ack=x+1 --- |
| |
| ---- ACK=1, | (我也收到了,开始传数据吧)
| seq=x+1, ack=y+1 -> |
| |为什么是三次?
- 第一次:客户端证明自己能发
- 第二次:服务器证明自己能收、能发
- 第三次:客户端证明自己能收
两次不够,因为服务器无法确认客户端是否收到了自己的回应。
2. 四次挥手(断开连接)
分手比握手复杂,因为双方可能还有数据没传完:
客户端 服务器
| |
| ---- FIN=1, seq=u ---> | (我没数据了,想断开)
| |
| <- ACK=1, ack=u+1 ---- | (知道了,但我可能还有数据)
| |
| [服务器继续发送数据] |
| |
| <- FIN=1, seq=w ------ | (我也没数据了,断开吧)
| |
| ---- ACK=1, ack=w+1 -> | (好的,再见)
| |TIME_WAIT 状态:客户端最后发送 ACK 后,会等待 2MSL(最大报文段生存时间),确保服务器的 FIN 能被收到,防止旧连接的数据包干扰新连接。
3. 可靠传输:ACK 与重传
TCP 给每个字节的数据编号(Sequence Number),接收方收到后回送确认(ACK)。如果发送方在一定时间内没收到 ACK,就认为丢包了,自动重传。
发送方:发送 seq=1-1000 的数据
接收方:回复 ack=1001(表示 1-1000 都收到了)
发送方:发送 seq=1001-2000
...
如果某段丢了:
接收方:回复 ack=1001(一直重复,表示还在等 1001)
发送方:超时重传 seq=1001-20004. 滑动窗口与流量控制
接收方通过 Window Size 字段告诉发送方自己还能接收多少字节。发送方据此调整发送速度,避免撑爆接收方的缓冲区。
接收方:Window = 64000 (我还有 64KB 空间)
发送方:一口气发 64KB
接收方:处理完一半,Window = 32000
发送方:放慢速度5. 拥塞控制
流量控制是针对接收方的,拥塞控制是针对网络的。TCP 通过四个算法动态调整发送速率:
| 算法 | 作用 |
|---|---|
| 慢启动(Slow Start) | 初始发送速率很低,指数增长探测网络容量 |
| 拥塞避免(Congestion Avoidance) | 接近阈值后改为线性增长 |
| 快重传(Fast Retransmit) | 收到 3 个重复 ACK 立即重传,不等超时 |
| 快恢复(Fast Recovery) | 丢包后不回到初始速度,从一半继续 |
拥塞窗口(cwnd) 和 慢启动阈值(ssthresh) 是控制核心。丢包时 ssthresh 设为当前 cwnd 的一半,cwnd 重置(或减半),然后重新探测。
三、TCP 报文头结构
TCP 报文头固定 20 字节,可选选项最多 40 字节:
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
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Source Port | Destination Port |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Sequence Number |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Acknowledgment Number |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Data | |U|A|P|R|S|F| |
| Offset| Reserved |R|C|S|S|Y|I| Window |
| | |G|K|H|T|N|N| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Checksum | Urgent Pointer |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Options | Padding |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| data |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+关键字段说明:
| 字段 | 长度 | 作用 |
|---|---|---|
| Source/Dest Port | 各 16bit | 标识发送/接收的应用程序 |
| Sequence Number | 32bit | 数据字节流的起始编号 |
| Ack Number | 32bit | 期望收到的下一个字节编号 |
| Window | 16bit | 接收窗口大小,用于流量控制 |
| Checksum | 16bit | 校验数据完整性 |
| Flags | 6bit | SYN、ACK、FIN、RST、PSH、URG |
四、TCP vs UDP
| 特性 | TCP | UDP |
|---|---|---|
| 连接 | 面向连接 | 无连接 |
| 可靠性 | 可靠,丢包重传 | 不可靠,尽力而为 |
| 顺序性 | 按序交付 | 不保证顺序 |
| 流量控制 | 有 | 无 |
| 拥塞控制 | 有 | 无 |
| 头部开销 | 20-60 字节 | 8 字节 |
| 传输效率 | 较低(有控制开销) | 极高 |
| 适用场景 | 文件传输、网页、邮件 | 视频直播、DNS、游戏 |
什么时候选 TCP,什么时候选 UDP?
用 TCP:数据完整性比速度重要——网页、文件下载、邮件、API 调用。
用 UDP:速度比可靠性重要——在线游戏、视频会议、直播、DNS 查询。
五、常见问题与排查
1. 为什么 TCP 连接会出现大量 TIME_WAIT?
主动关闭连接的一方会进入 TIME_WAIT。高并发短连接场景(如 HTTP/1.0)容易产生。解决方法:
- 启用连接池,复用 TCP 连接
- 调低
net.ipv4.tcp_tw_reuse参数(谨慎) - 升级到 HTTP/2 或 HTTP/3
2. 什么是粘包和拆包?
TCP 是字节流协议,不保留消息边界。发送两次 100 字节的数据,接收方可能一次读到 200 字节(粘包),也可能分三次读到(拆包)。
解决方法:在应用层定义消息格式,如固定长度、分隔符、长度前缀。
3. Nagle 算法与延迟 ACK
- Nagle 算法:小数据包攒一起发,减少网络开销
- 延迟 ACK:接收方等一会儿再回 ACK,看能不能 piggyback 在响应数据上
两者结合可能导致明显的延迟(如游戏操作卡顿)。可以通过 TCP_NODELAY 关闭 Nagle。
六、TCP 的演进
| 特性 | 传统 TCP | TCP Fast Open | Multipath TCP |
|---|---|---|---|
| 握手次数 | 3 次 | 1-2 次(带数据) | 多路径同时传输 |
| 场景 | 通用 | 降低延迟 | 移动设备 WiFi/4G 切换 |
HTTP/3 干脆放弃了 TCP,改用基于 UDP 的 QUIC 协议,在应用层实现可靠传输和快速握手。
总结
TCP 是互联网可靠传输的基石。它的三次握手、四次挥手、滑动窗口、拥塞控制等机制,构成了现代网络通信的底层保障。理解 TCP,对排查网络故障、优化应用性能、设计高并发系统都至关重要。
后续会介绍 UDP、IP、DNS 等相关协议,继续拆解网络通信的全貌。
网络协议系列第 3 篇 —— 由多多自动发布 🐾