4Computer Networks: A Systems Approach(第6版)

传输层

端到端通信

TCPUDP拥塞控制流量控制

🎯 AI 时代的学习建议:不要只读理论。理解这一章的设计动机(为什么需要这个概念?)和边界条件(什么情况下会失效?)。当 AI 生成的代码涉及这些概念时,你能判断它是否用对了场景。AI 能帮你总结内容,但无法替代你亲手阅读、思考、建立直觉的过程。

第4章:传输层(Transport Layer)

一、导读

1.1 本章学习目标

第4章讲解传输层——提供端到端的通信服务。TCP 和 UDP 是互联网最重要的两个传输层协议,本章深入分析它们的机制和设计权衡。

本章核心学习目标:

  • 掌握 TCP 的可靠传输机制:序号、确认、重传、滑动窗口
  • 理解 TCP 的拥塞控制:慢启动、拥塞避免、快重传、快恢复
  • 掌握 TCP 的连接管理:三次握手、四次挥手
  • 理解 UDP 的适用场景:为什么有些应用选择不可靠的传输?
  • 掌握流量控制和拥塞控制的区别

1.2 为什么本章如此重要

TCP 是互联网上最广泛使用的协议之一,理解其内部机制对于网络编程、性能调优和问题排查至关重要。

1.3 核心问题引导

TCP 为什么需要三次握手而不是两次?

拥塞控制的慢启动为什么是指数增长?

TCP 的公平性是如何实现的?


二、核心概念详解

2.1 TCP 可靠传输

TCP 的可靠传输机制:
1. 序号: 每个字节都有序号,按序交付
2. 确认: 接收方确认已收到的最高序号
3. 超时重传: 超时未确认则重传
4. 滑动窗口: 允许发送多个未确认的分组

累积确认:
ACK = 36 → 序号 ≤ 35 的所有字节已收到
下一个期望的字节序号 = 36

选择性确认 (SACK):
允许接收方告知哪些块已收到,哪些缺失
减少不必要的重传

2.2 TCP 拥塞控制

TCP Reno 拥塞控制:

慢启动 (Slow Start):
- cwnd 从 1 MSS 开始
- 每收到一个 ACK,cwnd += 1
- 效果: 每 RTT cwnd 翻倍(指数增长)
- 直到 cwnd ≥ ssthresh 或丢包

拥塞避免 (Congestion Avoidance):
- cwnd ≥ ssthresh 后进入
- 每 RTT cwnd += 1(线性增长)
- 直到丢包

快重传 (Fast Retransmit):
- 收到 3 个重复 ACK → 立即重传
- 不等待超时

快恢复 (Fast Recovery):
- 快重传后: ssthresh = cwnd/2, cwnd = ssthresh + 3
- 进入拥塞避免阶段

cwnd 变化示意:
     cwnd
      ↑    /\        /\
      |   /  \      /  \
      |  /    \    /    \
      | /      \  /      \
      |/        \/        \___
      +------------------------→ time
      |← 慢启动 →|← 拥塞避免 →|

2.3 TCP 连接管理

三次握手:
Client                Server
  |---SYN (seq=x)--->|
  |<--SYN+ACK (seq=y, ack=x+1)--|
  |---ACK (ack=y+1)->|
  |                   |
  |   连接建立         |

为什么需要三次?
- 两次: 无法确认 Client 收到了 Server 的 SYN+ACK
  → 可能导致 Server 以为连接已建立,但 Client 不知道
- 三次: 双方都确认了对方的初始序号

四次挥手:
Client                Server
  |---FIN (seq=u)--->|   Client 发送 FIN,进入 FIN_WAIT_1
  |<--ACK (ack=u+1)--|   Server 确认,Client 进入 FIN_WAIT_2
  |                   |   Server 继续发送剩余数据(半关闭)
  |<--FIN (seq=w)----|   Server 发送 FIN,进入 LAST_ACK
  |---ACK (ack=w+1)->|   Client 确认,进入 TIME_WAIT
  |                   |   Server 收到 ACK,进入 CLOSED
  |   (等待 2MSL)     |   Client 等待后进入 CLOSED

为什么需要 TIME_WAIT (2MSL)?
1. 确保最后的 ACK 到达 Server(如果丢失,Server 会重发 FIN)
2. 等待网络中该连接的残留分组消失(防止新连接收到旧数据)
MSL = Maximum Segment Lifetime = 2 分钟(RFC 793)

三、实战案例

3.1 案例一:TCP 拥塞控制模拟

python
class TCPCongestionControl:
    def __init__(self, mss=1460):
        self.cwnd = 1 * mss
        self.ssthresh = 65535
        self.mss = mss
        self.dup_acks = 0
    
    def on_ack(self, ack):
        """收到 ACK"""
        self.dup_acks = 0
        if self.cwnd < self.ssthresh:
            # 慢启动: 指数增长
            self.cwnd += self.mss
        else:
            # 拥塞避免: 线性增长
            self.cwnd += self.mss * self.mss // self.cwnd
    
    def on_dup_ack(self):
        """收到重复 ACK"""
        self.dup_acks += 1
        if self.dup_acks == 3:
            # 快重传
            self.ssthresh = max(self.cwnd // 2, 2 * self.mss)
            self.cwnd = self.ssthresh + 3 * self.mss
            self.dup_acks = 0
    
    def on_timeout(self):
        """超时"""
        self.ssthresh = max(self.cwnd // 2, 2 * self.mss)
        self.cwnd = self.mss
    
    def get_cwnd_mss(self):
        return self.cwnd // self.mss

# 模拟
tcp = TCPCongestionControl()
print("慢启动阶段:")
for i in range(6):
    print(f"  RTT {i}: cwnd = {tcp.get_cwnd_mss()} MSS")
    tcp.on_ack(0)

print("\n丢包 (超时):")
tcp.on_timeout()
print(f"  ssthresh = {tcp.ssthresh // tcp.mss}, cwnd = {tcp.get_cwnd_mss()}")

print("\n拥塞避免阶段:")
for i in range(5):
    tcp.on_ack(0)
    print(f"  RTT: cwnd = {tcp.get_cwnd_mss()} MSS")

四、本章小结

TCP 通过序号、确认、重传和滑动窗口实现可靠传输

拥塞控制通过调节发送速率避免网络过载,慢启动+拥塞避免实现加性增乘性减

三次握手确保双方同步初始序号,四次挥手支持半关闭状态


五、思考与练习

思考题

为什么 TCP 的慢启动是指数增长而不是线性增长?

在高 BDP 网络中,TCP Reno 的性能为什么很差?

编程练习

实现 TCP 滑动窗口协议,支持选择性确认(SACK)。

模拟 TCP 拥塞控制的 cwnd 变化,绘制曲线图。


4/7传输层
0%