TCP 与 UDP 深度剖析

从底层原理到实际应用,完整理解传输层两大核心协议的工作机制

01 协议概览

TCP 传输控制协议

  • 面向连接 - 传输前必须建立连接
  • 可靠传输 - 确认应答、超时重传
  • 流量控制 - 滑动窗口机制
  • 拥塞控制 - 慢启动、拥塞避免
  • 全双工 - 双向同时传输
  • 字节流 - 无消息边界

UDP 用户数据报协议

  • 无连接 - 直接发送数据
  • 不可靠 - 无确认、无重传
  • 无流量控制 - 发送即发送
  • 无拥塞控制 - 尽力而为
  • 高效轻量 - 头部仅8字节
  • 数据报 - 保留消息边界
🌐

OSI 七层模型

TCP/UDP 位于传输层(第4层),负责端到端的数据传输

🔄

TCP/IP 四层

对应传输层,向上为应用层,向下为网络层

🚪

端口号

16位端口(0-65535),标识不同应用进程

📦

复用/分用

多个应用共用网络,端口区分不同连接

02 UDP:简单即力量

UDP(User Datagram Protocol)是真正意义上的简单传输协议。 它只是简单地将数据报从源发送到目的地,不做任何额外处理。

UDP 头部结构(8字节)

源端口
16bit
目标端口
16bit
UDP长度
16bit
校验和
16bit
所有字段都是必要的
可选(IPv6中可选)
// UDP 头部各字段说明
struct udphdr {
    __be16  source;     // 源端口 - 发送方端口(可为零)
    __be16  dest;       // 目标端口 - 接收方端口(必填)
    __be16  len;        // UDP头部+数据的总长度
    __be16  check;      // 校验和(可选,IPv6必须)
};

极低延迟

无需连接建立,无握手延迟,首包即可传输数据

📦

数据报语义

保留完整消息边界,一个send对应一个recv

🔢

无状态

每个数据报独立处理,无连接状态开销

🎯

无队首阻塞

数据包独立,丢失一个不影响其他

03 TCP:可靠的传输艺术

TCP(Transmission Control Protocol)是互联网的支柱协议。 它通过精心设计的机制,在不可靠的IP网络上实现了可靠传输。

TCP 头部结构(20-60字节)

源端口
16bit
目标端口
16bit
序列号 Seq
32bit
确认号 Ack
32bit
偏移
4bit
保留
3bit
URG
ACK
PSH
RST
SYN
FIN
窗口大小
16bit
校验和
16bit
紧急指针
16bit
选项(可变)
0-40字节
核心可靠传输字段
标志位(6个控制位)
可选字段
🔢

序列号与确认应答

每个字节都有唯一序号,接收方确认已收数据

⏱️

超时重传

发送后等待确认,超时自动重发

🔄

三次握手

建立可靠连接的同步过程

👋

四次挥手

优雅关闭连接的同步过程

📊

滑动窗口

流量控制:避免发送过快

🚦

拥塞控制

避免网络过载的自我调节

04 三次握手:建立连接

TCP通过三次握手在不可靠的IP网络上建立可靠连接。 这是一个双方同步序列号的过程。

🔄 交互式动画演示
客户端
Client
服务端
Server

准备就绪

点击"开始动画"观看三次握手的完整过程

// 三次握手详细过程

┌─────────────────────────────────────────────────────────────────────┐
 第一次握手:客户端 → 服务端 SYN                    
  TCP头:SYN=1, Seq=x                                       
  含义:请求建立连接,客户端选择一个初始序列号 x           
└─────────────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────────────┐
 第二次握手:服务端 → 客户端 SYN+ACK               
  TCP头:SYN=1, ACK=1, Seq=y, Ack=x+1                       
  含义:同意连接,服务端选择自己的序列号 y,确认收到 x      
└─────────────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────────────┐
 第三次握手:客户端 → 服务端 ACK                   
  TCP头:ACK=1, Seq=x+1, Ack=y+1                            
  含义:确认收到,服务端可以开始发送数据                     
└─────────────────────────────────────────────────────────────────────┘

// 为什么是三次?不是两次或四次?
// 核心原因:TCP是全双工协议,双方都需要同步自己的初始序列号
// - 两次握手:服务端无法确认客户端是否收到了自己的SYN
// - 四次握手:多余了,第二次的SYN和ACK可以合并发送
🎲

初始序列号(ISN)

每次连接都会生成随机的ISN,防止历史残留数据干扰新连接

⚠️

SYN Flood 攻击

攻击者发送大量SYN但不完成握手,耗尽服务端资源

🛡️

SYN Cookie

防御SYN Flood:在第三次握手才分配资源

05 四次挥手:关闭连接

TCP连接关闭需要四次挥手,因为是全双工通信, 每个方向都需要单独关闭。

🔄 四次挥手动画
客户端
Client
服务端
Server

连接中

点击"开始动画"观看四次挥手的完整过程

// 四次挥手详细过程

┌─────────────────────────────────────────────────────────────────────┐
 第一次挥手:客户端 → 服务端 FIN                  
  含义:客户端发送FIN,请求关闭客户端→服务端的连接          
└─────────────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────────────┐
 第二次挥手:服务端 → 客户端 ACK                 
  含义:服务端确认收到FIN,但此时服务端可能还有数据要发送    
└─────────────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────────────┐
 第三次挥手:服务端 → 客户端 FIN                 
  含义:服务端数据发送完毕,发送FIN关闭服务→客户端的连接    
└─────────────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────────────┐
 第四次挥手:客户端 → 服务端 ACK                 
  含义:客户端确认收到FIN,等待2MSL后彻底关闭               
└─────────────────────────────────────────────────────────────────────┘

// TIME_WAIT 状态
// - 等待2MSL(Maximum Segment Lifetime,通常为60秒)
// - 目的1:确保最后的ACK能到达对端(如果丢失,对端会重发FIN)
// - 目的2:让本连接的所有数据包在网络中消散,避免干扰新连接

半关闭状态

一方发送FIN后仍可接收数据,直到对方也发送FIN

💀

RST 强制关闭

使用RST标志可立即关闭连接,但可能导致数据丢失

🔁

同时关闭

两端同时发送FIN,握手过程变为三次(特殊情况的四次合并)

06 滑动窗口:流量控制

滑动窗口机制让TCP实现流量控制, 确保发送方不会因为发送太快而导致接收方缓冲区溢出。

窗口可视化

已发送未确认
可用窗口
已确认
← 已发送 窗口大小: 5 个数据包 可用 →
// 滑动窗口工作原理

// 接收方在每次ACK中告诉发送方:rwnd(接收窗口大小)
发送方维护三个指针:
  1. SND.UNA - 第一个未确认的字节
  2. SND.NXT - 下一个要发送的字节
  3. SND.WND - 当前窗口大小

窗口范围:
  窗口左沿 = SND.UNA
  窗口右沿 = SND.UNA + SND.WND

窗口滑动条件:
  - 收到ACK时,左沿右移
  - 右沿右移条件:接收方有足够缓冲空间

// Zero Window(零窗口)问题
// 当接收方缓冲区满时,rwnd=0,发送方停止发送
// 接收方应用读取数据后,会发送一个rwnd>0的ACK
// 发送方收到后恢复发送,这个ACK称为 Window Update
📭

接收缓冲区

每个TCP socket都有接收缓冲区,存放到达的数据

📤

发送缓冲区

存储已发送但未确认的数据 + 已应用写入但未发送的数据

窗口探针

零窗口后,发送方定期发送1字节探针触发Window Update

07 拥塞控制:网络的守护者

拥塞控制是TCP最重要的特性之一,它让TCP能够感知网络状况, 自动调整发送速率,避免网络过载导致崩溃。

拥塞控制四阶段

0 慢启动 拥塞避免 快速恢复 吞吐量 → 时间
ssthresh
cwnd →
🐢

慢启动(Slow Start)

cwnd从1开始,每个ACK翻倍增长。指数增长,快速探测可用带宽。

🐇

拥塞避免(Congestion Avoidance)

cwnd达到ssthresh后,每个RTT只增加1个MSS。线性增长。

快速重传(Fast Retransmit)

收到3个重复ACK时,不等超时立即重传丢失的包。

🔄

快速恢复(Fast Recovery)

快速重传后,进入快速恢复,cwnd减半后进入拥塞避免。

// 拥塞控制核心算法

// 1. 慢启动
while (cwnd < ssthresh) {
    cwnd = cwnd * 2;  // 每个RTT翻倍
}
// 近似指数增长:1 → 2 → 4 → 8 → 16 → ...

// 2. 拥塞避免
while (cwnd >= ssthresh) {
    cwnd = cwnd + 1;  // 每个RTT增加1个MSS
}
// 线性增长:N → N+1 → N+2 → N+3 → ...

// 3. 检测拥塞
if (超时) {
    // 严重拥塞,重置连接
    ssthresh = cwnd / 2;
    cwnd = 1;
} else if (3个重复ACK) {
    // 轻度拥塞,部分丢包
    ssthresh = cwnd / 2;
    cwnd = ssthresh + 3;  // 快速恢复
}

08 协议对比与适用场景

🔵 TCP 适用场景

  • • HTTP/HTTPS(网页浏览)
  • • SMTP/POP3/IMAP(邮件)
  • • FTP(文件传输)
  • • SSH/Telnet(远程登录)
  • • 数据库连接(MySQL/PostgreSQL)
  • • API调用(RESTful)
Web Email File Database

🟢 UDP 适用场景

  • • DNS查询(通常使用UDP)
  • • DHCP(IP分配)
  • • VoIP(语音通话)
  • • 视频流(低延迟优先)
  • • 在线游戏
  • • QUIC协议(基于UDP)
DNS VoIP Video Gaming

性能对比

指标 TCP UDP
头部大小 20-60 字节 8 字节
延迟 较高(需握手) 最低(无握手)
可靠性 ✅ 100% ❌ 无
有序性 ✅ 保证 ❌ 不保证
流量控制 ✅ 滑动窗口 ❌ 无
拥塞控制 ✅ 四种算法 ❌ 无
单播/多播 仅单播 支持多播