Skip to content

TCP 和 UDP

一、TCP(传输控制协议)

1. TCP 的特点

面向连接 - 建立连接后才能通信
可靠传输 - 保证数据不丢失、不重复、有序到达
全双工通信 - 双向数据传输
流量控制 - 通过滑动窗口控制发送速度
拥塞控制 - 根据网络状况调整发送速率

2. TCP 三次握手

建立连接过程:

客户端                          服务端
  |                               |
  |----SYN(seq=x)--------------->|
  |                               | 进入 SYN_RCVD
  |<--SYN+ACK(seq=y, ack=x+1)----|
  |                               | 进入 ESTABLISHED
  |----ACK(ack=y+1)-------------->|
  |                               |
  进入 ESTABLISHED                |

详细说明:

  1. 第一次握手:客户端发送 SYN(seq=x)到服务端,客户端进入 SYN_SENT
  2. 第二次握手:服务端发送 SYN+ACK(seq=y, ack=x+1)到客户端,服务端进入 SYN_RCVD
  3. 第三次握手:客户端发送 ACK(ack=y+1)到服务端,双方进入 ESTABLISHED

为什么需要三次握手?

  • 防止旧的连接请求到达服务端(序列号验证)
  • 确保双方都能发送和接收数据(确认双方的发送和接收能力)
  • 同步初始序列号

如果只有两次握手会怎样?

  • 无法确认客户端的接收能力
  • 可能接受旧的连接请求,导致数据混乱

3. TCP 四次挥手

断开连接过程:

客户端                          服务端
  |                               |
  |----FIN(seq=x)--------------->|
  |                               | 进入 CLOSE_WAIT
  |<--ACK(ack=x+1)---------------|
  |                               |
  |<--FIN(seq=y)-----------------|
  |                               | 进入 LAST_ACK
  |----ACK(ack=y+1)-------------->|
  |                               | 进入 CLOSED
  进入 TIME_WAIT                 |
  等待 2MSL                      |
  进入 CLOSED                    |

详细说明:

  1. 第一次挥手:客户端发送 FIN(seq=x)到服务端,客户端进入 FIN_WAIT_1
  2. 第二次挥手:服务端发送 ACK(ack=x+1)到客户端,服务端进入 CLOSE_WAIT,客户端进入 FIN_WAIT_2
  3. 第三次挥手:服务端发送 FIN(seq=y)到客户端,服务端进入 LAST_ACK
  4. 第四次挥手:客户端发送 ACK(ack=y+1)到服务端,客户端进入 TIME_WAIT,服务端进入 CLOSED

为什么需要四次挥手?

  • 全双工通信,需要分别关闭发送和接收通道
  • 客户端发送 FIN,服务端可能还有数据要发送
  • 服务端发送完数据后再发送 FIN

TIME_WAIT 的作用?

  • 确保最后的 ACK 到达服务端(如果丢失,服务端会重发 FIN)
  • 防止旧连接的包影响新连接
  • 等待时间通常是 2MSL(Maximum Segment Lifetime)

为什么 TIME_WAIT 是 2MSL?

  • 确保 ACK 和可能的重传 FIN 都能在网络中消失
  • MSL 是数据包在网络中的最大生存时间

4. TCP 可靠传输机制

1. 确认应答(ACK)

  • 接收方收到数据后发送 ACK 确认
  • 序列号用于保证数据有序

2. 超时重传

  • 发送方等待 ACK 超时后重传数据
  • 超时时间根据网络状况动态调整(RTT - Round Trip Time)

3. 滑动窗口

  • 允许发送方连续发送多个数据包,无需等待每个 ACK
  • 接收方通过窗口大小控制发送方的发送速度

滑动窗口示例:

发送方窗口:[1][2][3][4][5]
            ↑已发送并确认
            ↑已发送未确认
            ↑可发送
            ↑窗口外

接收方窗口:[1][2][3][4]
            ↑已接收
            ↑可接收

4. 拥塞控制

  • 慢启动(Slow Start) - 初始拥塞窗口较小,指数增长
  • 拥塞避免(Congestion Avoidance) - 达到阈值后线性增长
  • 快速重传(Fast Retransmit) - 收到 3 个重复 ACK 后立即重传
  • 快速恢复(Fast Recovery) - 快速重传后进入恢复阶段

拥塞控制算法:

1. 慢启动阶段:
   cwnd = 1, 2, 4, 8, 16, ... (指数增长)
   
2. 达到阈值(ssthresh)后进入拥塞避免:
   cwnd = cwnd + 1/cwnd (线性增长)
   
3. 检测到拥塞:
   ssthresh = cwnd / 2
   cwnd = 1 (重新慢启动)

5. TCP 头部结构

 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        |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

关键字段:

  • Source Port / Destination Port - 源端口/目标端口
  • Sequence Number - 序列号
  • Acknowledgment Number - 确认号
  • Data Offset - 数据偏移
  • Flags - 控制标志(SYN、ACK、FIN、RST 等)
  • Window - 窗口大小
  • Checksum - 校验和

二、UDP(用户数据报协议)

1. UDP 的特点

无连接 - 不需要建立连接
快速 - 开销小,速度快
简单 - 协议简单
不可靠 - 不保证数据到达
无序 - 不保证数据顺序

2. UDP 头部结构

 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        |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|            Length             |           Checksum            |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

字段:

  • Source Port - 源端口(可选)
  • Destination Port - 目标端口
  • Length - UDP 头部和数据长度
  • Checksum - 校验和(可选)

3. UDP 的应用场景

适用场景:

  • DNS 查询 - 快速查询,失败可重试
  • 视频流 - 允许少量丢包
  • 在线游戏 - 低延迟要求
  • 广播/组播 - 一对多通信
  • 实时通信 - 语音、视频通话

不适用场景:

  • ❌ 文件传输(需要可靠性)
  • ❌ 网页浏览(需要可靠性)
  • ❌ 数据库连接(需要可靠性)

三、TCP vs UDP

1. 对比表

特性TCPUDP
连接面向连接无连接
可靠性可靠不可靠
有序性有序无序
速度较慢较快
开销较大较小
头部大小20 字节8 字节
流量控制
拥塞控制
应用场景HTTP、FTP、SMTPDNS、视频流、游戏

2. 选择原则

选择 TCP:

  • 需要可靠传输
  • 需要有序传输
  • 可以接受延迟
  • 文件传输、网页浏览

选择 UDP:

  • 需要低延迟
  • 可以接受少量丢包
  • 实时性要求高
  • 视频流、在线游戏

3. 性能对比

TCP:

  • 建立连接需要时间(三次握手)
  • 需要维护连接状态
  • 需要确认和重传
  • 有拥塞控制,可能降低速度

UDP:

  • 无需建立连接
  • 无状态,开销小
  • 无确认和重传
  • 无拥塞控制,速度更快

四、TCP 状态转换

1. TCP 状态

客户端状态:

  • CLOSED - 关闭
  • SYN_SENT - 发送 SYN 后
  • ESTABLISHED - 连接建立
  • FIN_WAIT_1 - 发送 FIN 后
  • FIN_WAIT_2 - 收到 FIN 的 ACK 后
  • TIME_WAIT - 等待 2MSL
  • CLOSED - 关闭

服务端状态:

  • CLOSED - 关闭
  • LISTEN - 监听
  • SYN_RCVD - 收到 SYN 后
  • ESTABLISHED - 连接建立
  • CLOSE_WAIT - 收到 FIN 后
  • LAST_ACK - 发送 FIN 后
  • CLOSED - 关闭

2. 状态转换图

客户端:
CLOSED → SYN_SENT → ESTABLISHED → FIN_WAIT_1 → FIN_WAIT_2 → TIME_WAIT → CLOSED

服务端:
CLOSED → LISTEN → SYN_RCVD → ESTABLISHED → CLOSE_WAIT → LAST_ACK → CLOSED

五、TCP 优化

1. 连接优化

TCP_NODELAY:

csharp
// 禁用 Nagle 算法,减少延迟
socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true);

Keep-Alive:

csharp
// 保持连接活跃,检测连接是否断开
socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true);

2. 缓冲区优化

接收缓冲区:

csharp
socket.ReceiveBufferSize = 65535;

发送缓冲区:

csharp
socket.SendBufferSize = 65535;

3. 超时设置

csharp
socket.ReceiveTimeout = 5000;  // 5 秒
socket.SendTimeout = 5000;     // 5 秒

六、常见面试题

Q1: 为什么 TCP 是可靠的?

机制:

  1. 确认应答 - 接收方确认收到数据
  2. 超时重传 - 未收到确认则重传
  3. 序列号 - 保证数据有序
  4. 校验和 - 检测数据错误
  5. 流量控制 - 防止接收方缓冲区溢出
  6. 拥塞控制 - 防止网络拥塞

Q2: TCP 三次握手的详细过程?

  1. 第一次握手:客户端发送 SYN(seq=x),进入 SYN_SENT
  2. 第二次握手:服务端发送 SYN+ACK(seq=y, ack=x+1),进入 SYN_RCVD
  3. 第三次握手:客户端发送 ACK(ack=y+1),双方进入 ESTABLISHED

为什么需要三次?

  • 确认双方的发送和接收能力
  • 同步初始序列号
  • 防止旧的连接请求

Q3: TCP 四次挥手的详细过程?

  1. 第一次挥手:客户端发送 FIN,进入 FIN_WAIT_1
  2. 第二次挥手:服务端发送 ACK,进入 CLOSE_WAIT,客户端进入 FIN_WAIT_2
  3. 第三次挥手:服务端发送 FIN,进入 LAST_ACK
  4. 第四次挥手:客户端发送 ACK,进入 TIME_WAIT,服务端进入 CLOSED

为什么需要四次?

  • 全双工通信,需要分别关闭
  • 服务端可能还有数据要发送

Q4: TIME_WAIT 的作用?

  1. 确保 ACK 到达 - 如果 ACK 丢失,服务端会重发 FIN
  2. 防止旧连接影响 - 等待旧连接的包在网络中消失
  3. 等待时间 - 通常是 2MSL

Q5: TCP 的拥塞控制算法?

四个阶段:

  1. 慢启动 - 指数增长(cwnd = 1, 2, 4, 8, ...)
  2. 拥塞避免 - 线性增长(cwnd = cwnd + 1/cwnd)
  3. 快速重传 - 收到 3 个重复 ACK 后立即重传
  4. 快速恢复 - 快速重传后进入恢复阶段

Q6: UDP 如何实现可靠传输?

应用层实现:

  • 添加序列号
  • 添加确认机制
  • 添加重传机制
  • 添加超时机制

示例:

csharp
// 应用层协议
public class ReliableUdpPacket
{
    public int SequenceNumber { get; set; }
    public byte[] Data { get; set; }
    public DateTime Timestamp { get; set; }
}

// 发送方维护未确认的包
var unackedPackets = new Dictionary<int, ReliableUdpPacket>();

// 接收方发送确认
var ack = new AckPacket { SequenceNumber = receivedPacket.SequenceNumber };
SendAck(ack);

Q7: TCP 和 UDP 的头部大小?

  • TCP 头部 - 20 字节(最小),最大 60 字节(有选项)
  • UDP 头部 - 8 字节(固定)

Q8: 如何选择 TCP 还是 UDP?

选择 TCP:

  • 需要可靠传输(文件传输、网页浏览)
  • 可以接受延迟
  • 需要有序传输

选择 UDP:

  • 需要低延迟(游戏、视频通话)
  • 可以接受少量丢包
  • 实时性要求高

七、实际应用

1. TCP 应用

  • HTTP/HTTPS - 网页浏览
  • FTP - 文件传输
  • SMTP - 邮件发送
  • SSH - 远程登录
  • 数据库连接 - MySQL、PostgreSQL

2. UDP 应用

  • DNS - 域名解析
  • DHCP - 动态 IP 分配
  • 视频流 - 直播、点播
  • 在线游戏 - 实时游戏
  • VoIP - 语音通话

3. 混合使用

QUIC(HTTP/3):

  • 基于 UDP
  • 在应用层实现可靠性
  • 更快连接建立
  • 改进的拥塞控制

八、最佳实践

  1. 合理选择协议 - 根据需求选择 TCP 或 UDP
  2. 优化 TCP 参数 - 调整缓冲区、超时等
  3. 使用连接池 - 复用 TCP 连接
  4. 监控网络 - 关注延迟、丢包率
  5. 错误处理 - 正确处理网络错误
  6. 超时设置 - 避免无限等待
  7. 重试机制 - 网络故障时重试
  8. 不要忽略错误 - 正确处理异常
  9. 不要过度优化 - 根据实际需求优化
  10. 不要忽略安全 - 使用 TLS/SSL 加密

基于 VitePress 构建 | Copyright © 2026-present