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 |详细说明:
- 第一次握手:客户端发送 SYN(seq=x)到服务端,客户端进入 SYN_SENT
- 第二次握手:服务端发送 SYN+ACK(seq=y, ack=x+1)到客户端,服务端进入 SYN_RCVD
- 第三次握手:客户端发送 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 |详细说明:
- 第一次挥手:客户端发送 FIN(seq=x)到服务端,客户端进入 FIN_WAIT_1
- 第二次挥手:服务端发送 ACK(ack=x+1)到客户端,服务端进入 CLOSE_WAIT,客户端进入 FIN_WAIT_2
- 第三次挥手:服务端发送 FIN(seq=y)到客户端,服务端进入 LAST_ACK
- 第四次挥手:客户端发送 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. 对比表
| 特性 | TCP | UDP |
|---|---|---|
| 连接 | 面向连接 | 无连接 |
| 可靠性 | 可靠 | 不可靠 |
| 有序性 | 有序 | 无序 |
| 速度 | 较慢 | 较快 |
| 开销 | 较大 | 较小 |
| 头部大小 | 20 字节 | 8 字节 |
| 流量控制 | ✅ | ❌ |
| 拥塞控制 | ✅ | ❌ |
| 应用场景 | HTTP、FTP、SMTP | DNS、视频流、游戏 |
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- 等待 2MSLCLOSED- 关闭
服务端状态:
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 是可靠的?
机制:
- 确认应答 - 接收方确认收到数据
- 超时重传 - 未收到确认则重传
- 序列号 - 保证数据有序
- 校验和 - 检测数据错误
- 流量控制 - 防止接收方缓冲区溢出
- 拥塞控制 - 防止网络拥塞
Q2: TCP 三次握手的详细过程?
- 第一次握手:客户端发送 SYN(seq=x),进入 SYN_SENT
- 第二次握手:服务端发送 SYN+ACK(seq=y, ack=x+1),进入 SYN_RCVD
- 第三次握手:客户端发送 ACK(ack=y+1),双方进入 ESTABLISHED
为什么需要三次?
- 确认双方的发送和接收能力
- 同步初始序列号
- 防止旧的连接请求
Q3: TCP 四次挥手的详细过程?
- 第一次挥手:客户端发送 FIN,进入 FIN_WAIT_1
- 第二次挥手:服务端发送 ACK,进入 CLOSE_WAIT,客户端进入 FIN_WAIT_2
- 第三次挥手:服务端发送 FIN,进入 LAST_ACK
- 第四次挥手:客户端发送 ACK,进入 TIME_WAIT,服务端进入 CLOSED
为什么需要四次?
- 全双工通信,需要分别关闭
- 服务端可能还有数据要发送
Q4: TIME_WAIT 的作用?
- 确保 ACK 到达 - 如果 ACK 丢失,服务端会重发 FIN
- 防止旧连接影响 - 等待旧连接的包在网络中消失
- 等待时间 - 通常是 2MSL
Q5: TCP 的拥塞控制算法?
四个阶段:
- 慢启动 - 指数增长(cwnd = 1, 2, 4, 8, ...)
- 拥塞避免 - 线性增长(cwnd = cwnd + 1/cwnd)
- 快速重传 - 收到 3 个重复 ACK 后立即重传
- 快速恢复 - 快速重传后进入恢复阶段
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
- 在应用层实现可靠性
- 更快连接建立
- 改进的拥塞控制
八、最佳实践
- ✅ 合理选择协议 - 根据需求选择 TCP 或 UDP
- ✅ 优化 TCP 参数 - 调整缓冲区、超时等
- ✅ 使用连接池 - 复用 TCP 连接
- ✅ 监控网络 - 关注延迟、丢包率
- ✅ 错误处理 - 正确处理网络错误
- ✅ 超时设置 - 避免无限等待
- ✅ 重试机制 - 网络故障时重试
- ❌ 不要忽略错误 - 正确处理异常
- ❌ 不要过度优化 - 根据实际需求优化
- ❌ 不要忽略安全 - 使用 TLS/SSL 加密