TCP的工作原理
2026/7/23大约 4 分钟
TCP的工作原理
TCP(传输控制协议)的工作原理可以用一句话概括:在不可靠的 IP 网络之上,建立一个可靠的、面向连接的“虚拟管道”。
为了保证数据不丢、不错、不乱,TCP 设计了一整套复杂的机制。我们可以将其拆解为三个核心阶段:
1. 建立连接:三次握手 (Three-way Handshake)
在传输数据前,双方必须确认彼此的收发能力。这就像打长途电话前的确认:
- 第一次握手:客户端发送
SYN(同步)报文,询问:“你能听到我说话吗?” - 第二次握手:服务端返回
SYN + ACK(确认)报文,回答:“我听到了,你能听到我吗?” - 第三次握手:客户端返回
ACK报文,确认:“我也听到了。那我们开始吧!”
2. 数据传输:可靠性“全家桶”
连接建立后,TCP 通过以下四种武器确保数据万无一失:
- 序列号与确认应答 (ACK):给每个字节都编上号。接收方收到后回传一个“收到第 X 号之前的所有数据”的信号。如果发送方没收到 ACK,就会超时重传。
- 校验和 (Checksum):检测数据在路上有没有被电磁干扰“弄脏”。如果对不上,直接丢弃要求重发。
- 流量控制 (Flow Control):利用滑动窗口机制。接收方会告诉发送方:“我现在的缓存快满了,请发慢一点”,防止接收端被数据淹没。
- 拥塞控制 (Congestion Control):TCP 会试探网络的承受能力。如果发现丢包率上升,它会主动减慢发送速度(慢启动、拥塞避免),防止整个互联网发生“大堵车”。
3. 断开连接:四次挥手 (Four-way Waves)
由于 TCP 是全双工的(双方都能同时发数据),断开时需要双方都确认关闭自己的发送通道:
- 第一次挥手:客户端发
FIN,说:“我的数据发完了,我要关了。” - 第二次挥手:服务端回
ACK:“收到,但我还有点东西没发完,你等下。”(此时客户端进入半关闭状态)。 - 第三次挥手:服务端发完数据后,发
FIN:“我也发完了,正式拜拜。” - 第四次挥手:客户端回
ACK:“好的,拜拜。” 客户端会等待一小段时间(2MSL)确保服务端收到了最后这条消息,然后彻底释放资源。
4.连接的队列(Queue)
Recv-Q
- 在 Established(已连接)状态下: Recv-Q 表示已经在本地系统的内核缓冲区中接收到,但尚未被应用程序(如 Nginx、Python 程序等)调用
read()或recv()取走的字节数。 - 在 Listen(监听)状态下: Recv-Q 的含义会发生变化,它表示 全连接队列(Accept Queue) 的当前长度,即已经完成 TCP 三次握手、等待应用程序调用
accept()的连接数。
Send-Q
TCP 协议为了保证传输的可靠性,有一个“确认机制”。当你发送一段数据时,它不会立刻从内存中删除,而是先呆在 Send-Q 里,直到满足以下条件:
- 物理层发出了数据:数据已经通过网卡传向网络。
- 对方回传了 ACK:对方确认收到了这段数据。
只有收到 ACK 后,这部分数据才会从 Send-Q 中清除。
为什么 Send-Q 会堆积?
如果 Send-Q 的数值长期很大,通常意味着**“发出去的东西对方没收到”或“发得太快,网络塞车了”**。常见原因包括:
- 网络带宽受限:你的发送速度超过了网卡的物理带宽或运营商的限速。
- 网络拥塞/丢包:数据包在传输途中丢了,导致 TCP 不断重传,数据积压在缓冲区。
- 接收端处理太慢:对方的 Recv-Q 满了(通告窗口为 0),明确告诉你“别发了,我处理不过来”,导致你的数据只能堵在发件箱里。
- 对方宕机或断网:你一直在尝试发送和重传,但对方没有任何响应。
Recv-Q 与 Send-Q 的对比
为了让你更直观地理解,我们可以把 Linux 内核协议栈想象成一个中间仓库:
| 队列名称 | 含义 | 堆积的后果 |
|---|---|---|
| Recv-Q | 外部发来的、存在内核里、程序还没读的数据。 | 缓冲区满了会导致远程端发送失败(零窗口),表现为网络卡顿。 |
| Send-Q | 程序发出的、存在内核里、对方还没确认接收的数据。 | 说明网络带宽不足、拥塞,或者对方接收能力太差。 |
