5.1 UDP 协议

UDP (User Datagram Protocol)
无连接、不可靠、面向报文的传输层协议。报文不编号、不建立连接、不拥塞控制,首部仅 8 字节,开销小、实时性好。

UDP 报文首部:源端口(2B) + 目的端口(2B) + 长度(2B) + 校验和(2B)。

对比项UDPTCP
连接无连接面向连接
可靠性不可靠,尽最大努力可靠,确认/重传/排序
首部开销8 字节20 字节(最小)
拥塞/流量控制
通信方式支持单播/多播/广播仅点对点
应用DNS、DHCP、视频流HTTP、FTP、SMTP

5.2 TCP 报文段

TCP 面向连接、可靠传输、全双工、点对点。TCP 报文段首部 20~60 字节,重要字段:

字段长度说明
源端口/目的端口各 2B标识应用进程
序号 seq4B本报文数据首字节的序号
确认号 ack4B期望收到对方下一字节的序号
数据偏移4 位首部长度(以 4B 为单位)
控制位6 位URG/ACK/PSH/RST/SYN/FIN
窗口 rwnd2B接收端通告的可用缓存(流量控制)
校验和2B检验首部+数据
紧急指针2BURG=1 时有效

5.3 TCP 连接管理 — 三次握手

TCP 建立连接需三次握手,确保双方收发能力正常。

为何三次?两次握手无法防止已失效的连接请求到达服务端导致资源浪费;三次握手能让双方都确认对方收发正常,且同步初始序号 (ISN)。

5.4 TCP 连接释放 — 四次挥手

TCP 释放连接需四次挥手,因 TCP 全双工,每方向需单独关闭。

TIME_WAIT 与 2MSL:主动关闭方进入 TIME_WAIT 后需等待 2MSL(最大报文段寿命),原因:①保证最后 ACK 能到达对端(若丢失可重传);②防止本次连接的报文残留干扰新连接。

5.5 可靠传输、流量控制与滑动窗口

TCP 通过序号+确认+重传实现可靠传输。采用累计确认机制,确认号表示此前所有字节均已正确接收。

流量控制
接收方通过 ACK 报文中的窗口字段 rwnd 通告自身可用缓存,发送方据此调整发送窗口,防止接收方被淹没。
流量控制 vs 拥塞控制:流量控制是端到端的,抑制发送方以适应接收方;拥塞控制是全局的,抑制发送方以适应网络。

TCP 可靠传输的滑动窗口机制与数据链路层类似(见第 3 章),但基于字节流且窗口动态变化。

5.6 TCP 拥塞控制

拥塞控制通过调节拥塞窗口 cwnd 控制发送速率。发送窗口 = min(rwnd, cwnd)。四种算法:

算法规则
慢开始cwnd 从 1 起按 2 的倍数指数增长(每 RTT 翻倍),到 ssthresh 改用拥塞避免
拥塞避免cwnd 每经过一个 RTT 线性加 1(加性增)
快重传收到 3 个重复 ACK 立即重传丢失报文,不等超时
快恢复3 重复 ACK 时:ssthresh = cwnd/2,cwnd = ssthresh,进入拥塞避免(非从 1 开始)
超时 vs 3 重复 ACK:超时认为拥塞严重 → cwnd=1 重新慢开始;3 重复 ACK 认为拥塞较轻 → cwnd 减半进入快恢复。

5.7 TCP 校验和计算

TCP 校验和用于检测报文在传输过程中是否出现差错,其计算范围覆盖首部 + 数据 + 伪首部三部分。

TCP 校验和计算范围
TCP 校验和对整个报文段进行计算,包括:① TCP 首部(20~60 字节);② TCP 数据部分;③ 伪首部(12 字节,仅参与校验和计算,不实际传输)。

伪首部的组成(12 字节):

字段长度说明
源 IP 地址4 字节发送方 IP 地址
目的 IP 地址4 字节接收方 IP 地址
协议号2 字节TCP = 6(UDP = 17)
TCP 长度2 字节TCP 首部 + 数据的总长度(不含伪首部)
伪首部的作用:伪首部将网络层 IP 地址与传输层数据关联起来,确保报文确实到达了正确的目的地。若目的 IP 地址不匹配,校验和将不通过。

校验和计算步骤:

步骤操作
1将伪首部、TCP 首部和数据拼接成一个整体(若数据长度为奇数,末尾补 1 字节 0)
2将整个数据块按 16 位(2 字节)分组
3对所有 16 位分组进行二进制反码求和(按反码相加,若有进位则回卷到最低位)
4对结果取反(1 → 0,0 → 1),即得到校验和
5发送方将校验和填入 TCP 首部的校验和字段;接收方收到后重新计算,若结果为 0 则校验通过

5.8 TCP 超时重传机制

往返时间 RTT (Round-Trip Time)
从发送方发送报文段到收到接收方确认所经历的时间。由于网络负载和路由变化,RTT 是动态波动的。

平滑 RTT(SRTT):TCP 不直接使用一次测量的 RTT,而是对多次测量进行加权平均,以获得更稳定的 RTT 估计值:

公式说明
SRTT = α × SRTT + (1-α) × RTT_sampleα 通常取 0.875(RFC 6298),SRTT 为平滑后的 RTT,RTT_sample 为新测量值
RTTVAR = β × RTTVAR + (1-β) × |RTT_sample - SRTT|RTTVAR 为 RTT 偏差的平滑估计,β 通常取 0.25

超时重传时间 RTO(Retransmission Timeout)

RTO 计算公式(RFC 6298):RTO = SRTT + max(G, K × RTTVAR),其中 G 为网络固有延迟(最小 1 秒),K 通常取 4。首次测量时 RTO 初始值取 3 秒。
Karn 算法
当报文段被重传时,发送方无法区分确认是针对原始报文还是重传报文。Karn 算法规定:不对重传报文的 RTT 进行测量,仅对首次传输的报文测量 RTT,避免 RTT 估计失真。若确实需要在重传时测量,可使用时间戳选项(RFC 7323)。

快速重传与快速恢复的触发条件:

触发条件TCP 反应
超时计时器到期拥塞严重 → cwnd = 1,ssthresh = cwnd/2,重新进入慢开始
收到 3 个重复 ACK(同一确认号)拥塞较轻 → 触发快重传(立即重传,不等超时)+ 快恢复

5.9 TCP 拥塞控制详解

以下给出慢开始、拥塞避免、快重传与快恢复算法的详细伪代码与流程。

算法:慢开始(Slow Start)
cwnd = 1                       // 拥塞窗口初始化为 1 个 MSS
ssthresh = 64                  // 慢开始阈值,默认 64KB

function slow_start():
    // 每收到一个新的 ACK(累计确认推进)
    // cwnd 按指数增长,每 RTT 翻倍
    for each ACK that acknowledges new data:
        if cwnd < ssthresh:
            cwnd = cwnd + 1    // 指数增长阶段
        else:
            // 达到阈值,切换到拥塞避免
            congestion_avoidance()

    // 发送窗口 = min(rwnd, cwnd)
    // 当 cwnd >= ssthresh 时退出慢开始
算法:拥塞避免(Congestion Avoidance)
function congestion_avoidance():
    // 每 RTT 线性加 1(加性增)
    for each ACK that acknowledges new data:
        // cwnd 每收到一个 ACK 增加 1/cwnd
        // 经过一个完整 RTT 后,cwnd 增加约 1
        cwnd = cwnd + 1 / cwnd    // 精确实现:每个 ACK 加 1/cwnd
        if cwnd >= ssthresh:
            // 保持在拥塞避免阶段
            pass

    // 检测到拥塞时(超时或 3 重复 ACK):
    //   ssthresh = max(cwnd/2, 2)
    //   超时:cwnd = 1,重新慢开始
    //   3 重复 ACK:cwnd = ssthresh,快恢复
算法:快重传与快恢复(Fast Retransmit & Fast Recovery)
duplicate_ack_count = 0

function on_duplicate_ack(ack_number):
    duplicate_ack_count = duplicate_ack_count + 1

    // 触发快重传:收到 3 个重复 ACK
    if duplicate_ack_count == 3:
        ssthresh = max(cwnd / 2, 2)    // 阈值减半
        cwnd = ssthresh                 // 窗口减半
        // 立即重传丢失的报文段
        retransmit(ack_number)
        enter_fast_recovery()

function enter_fast_recovery():
    // 快恢复阶段:每收到一个重复 ACK,cwnd 加 1
    // (因为重复 ACK 说明部分数据仍在流动中)
    for each duplicate ACK after the 3rd:
        cwnd = cwnd + 1
        // 继续重传下一个未确认的报文

    // 当收到新的 ACK(确认了丢失数据):
    // cwnd = ssthresh
    // 回到拥塞避免阶段

function on_timeout():
    // 超时:拥塞严重
    ssthresh = max(cwnd / 2, 2)
    cwnd = 1
    duplicate_ack_count = 0
    enter_slow_start()
快恢复 vs 慢开始:3 个重复 ACK 后,快恢复将 cwnd 减半而非重置为 1,因为重复 ACK 表明网络仍有一定承载能力,拥塞相对较轻。超时则表明网络拥塞严重,必须从 1 开始重新慢开始。

拥塞控制状态转换图: