一、TCP三次握手(建立连接)
握手过程
客户端 服务器
| |
| ----------- SYN=1, seq=x ----------> |
| | 进入SYN_RCVD状态
| <---- SYN=1, ACK=1, seq=y, ack=x+1 - |
| |
| ---------- ACK=1, ack=y+1 ---------> |
| | 进入ESTABLISHED状态
进入ESTABLISHED状态 |
详细步骤
第一次握手(SYN)
- 客户端 发送连接请求报文
- 设置标志位
SYN=1 - 随机生成初始序列号
seq=x - 客户端进入 SYN_SENT 状态
第二次握手(SYN+ACK)
- 服务器 收到SYN报文后
- 发送确认报文:
SYN=1, ACK=1 - 随机生成自己的序列号
seq=y - 确认号
ack=x+1 - 服务器进入 SYN_RCVD 状态
第三次握手(ACK)
- 客户端 收到SYN+ACK报文
- 发送确认报文:
ACK=1 - 序列号
seq=x+1 - 确认号
ack=y+1 - 双方进入 ESTABLISHED 状态
为什么需要三次握手?
- 防止历史连接:避免旧的重复连接请求造成混乱
- 同步序列号:双方交换初始序列号,确保可靠传输
- 验证双方能力:确认双方的发送和接收能力都正常
二、TCP四次挥手(断开连接)
挥手过程
客户端 服务器
| |
| ---------- FIN=1, seq=u -----------> |
| | 进入CLOSE_WAIT状态
| <----- ACK=1, seq=v, ack=u+1 ------- |
| |
| ... (数据传输) ... |
| | 进入LAST_ACK状态
| <------- FIN=1, ACK=1, seq=w ------- |
| |
| ------ ACK=1, seq=u+1, ack=w+1 ----> |
| | 进入CLOSED状态
进入TIME_WAIT状态 |
(等待2MSL后进入CLOSED)
详细步骤
第一次挥手(FIN)
- 主动关闭方 发送FIN报文
- 设置
FIN=1,携带序列号seq=u - 进入 FIN_WAIT_1 状态
第二次挥手(ACK)
- 被动关闭方 收到FIN报文
- 发送确认报文:
ACK=1 - 确认号
ack=u+1 - 进入 CLOSE_WAIT 状态
- 主动方收到ACK后进入 FIN_WAIT_2 状态
第三次挥手(FIN)
- 被动关闭方 发送FIN报文
- 设置
FIN=1, ACK=1 - 携带序列号
seq=w - 进入 LAST_ACK 状态
第四次挥手(ACK)
- 主动关闭方 收到FIN报文
- 发送确认报文:
ACK=1 - 确认号
ack=w+1 - 进入 TIME_WAIT 状态
- 被动方收到ACK后立即进入 CLOSED 状态
- 主动方等待 2MSL 后进入 CLOSED 状态
为什么需要四次挥手?
- TCP是全双工的:每个方向都需要单独关闭
- 半关闭状态:允许一方在发送完数据后关闭发送通道,但仍可接收数据
- 确保数据完整性:保证所有数据都被正确处理
TIME_WAIT状态的作用
- 可靠终止:确保最后的ACK能被对方收到
- 避免旧连接干扰:让所有报文在网络中消失,防止被新连接误接收
三、流量控制(Flow Control)
1. 目的
防止发送方发送数据过快,超过接收方的处理能力,导致接收方缓冲区溢出和数据丢失。
2. 实现机制:滑动窗口协议
text
发送方 接收方
| |
| ----- 窗口大小=4000 bytes -----> |
| | 通告接收窗口(rwnd)
| <--- ACK, rwnd=2000 bytes ---- |
| | (缓冲区变满)
| ----- 发送2000字节数据 ------> |
| |
| <--- ACK, rwnd=0 ----------- | 缓冲区已满
| |
| (停止发送) |
| | 应用程序读取数据
| <--- ACK, rwnd=3000 --------- | 缓冲区有空闲
| |
| ----- 继续发送数据 ---------> |
3. 关键概念
(1)接收窗口(RWND)
- 接收方在ACK报文中通告的可用缓冲区大小
- 计算公式:
RWND = 接收方缓冲区大小 - 已用缓冲区大小
(2)发送窗口(SWND)
- 发送方实际使用的窗口大小
- 计算公式:
SWND = min(RWND, CWND)(CWND为拥塞窗口)
(3)零窗口问题与解决方案
问题:当接收方缓冲区满时,通告窗口为0,发送方停止发送
死锁风险:接收方后续发送的非零窗口更新可能丢失
解决方案:
-
零窗口探测:
- 发送方定时(如5-60秒)发送1字节的探测报文
- 接收方用当前窗口大小响应
- 确认接收方是否恢复接收能力
-
持续定时器:
- 发送方维护一个定时器
- 窗口为0时启动,超时后发送探测报文
(4)糊涂窗口综合征(Silly Window Syndrome)
问题:双方交互小数据包,网络效率极低
发送方每次发送1字节,接收方每次通告1字节窗口
导致大量TCP头部开销(20-60字节)传输少量数据
解决方案:
-
接收方策略:不通告小窗口
- 窗口小于
min(MSS, 缓冲区大小/2)时,通告窗口为0 - 等待缓冲区有足够空间再通告
- 窗口小于
-
发送方策略:Nagle算法
规则: 1. 如果有未确认数据,缓存小数据包 2. 直到收到所有未确认数据的ACK,或累积到MSS大小 3. 才发送数据 例外情况: - 紧急数据立即发送 - 某些实时应用禁用Nagle算法
四、拥塞控制(Congestion Control)
1. 目的
防止网络过载,通过控制发送速率避免路由器队列溢出和数据包丢失。
2. 核心概念:拥塞窗口(CWND)
- 发送方维护的内部变量
- 表示在当前网络条件下可以发送但未确认的最大数据量
- 实际发送窗口 = min(CWND, RWND)
3. TCP拥塞控制四大核心算法
(1)慢启动(Slow Start)
目标:快速探测网络容量
规则:
1. 初始CWND = 1 MSS(通常10-1460字节)
2. 每收到一个ACK,CWND增加1 MSS
3. 每RTT时间,CWND翻倍(指数增长)
结束条件:
- CWND >= ssthresh(慢启动阈值)
- 发生超时重传(RTO)
- 收到3个重复ACK
(2)拥塞避免(Congestion Avoidance)
目标:缓慢增长,避免触发拥塞
规则:
1. 当CWND >= ssthresh时进入
2. 每收到一个ACK,CWND增加 1/CWND MSS
3. 每RTT时间,CWND增加1 MSS(线性增长)
(3)快重传(Fast Retransmit)
目标:快速恢复单个丢包,避免等待超时
触发条件:收到3个重复ACK
处理过程:
1. 立即重传重复ACK对应的数据包
2. 不等待RTO超时
3. 进入快恢复阶段
(4)快恢复(Fast Recovery)
目标:在快重传后,快速恢复数据传输
过程:
1. ssthresh = max(未确认数据量/2, 2*MSS)
2. CWND = ssthresh + 3*MSS(因3个重复ACK)
3. 每收到一个重复ACK,CWND增加1 MSS
4. 收到新数据的ACK时,CWND = ssthresh
5. 进入拥塞避免阶段
4. 拥塞控制状态机
初始:慢启动
↓
CWND >= ssthresh → 拥塞避免
↓
超时发生 → CWND=1,ssthresh=CWND/2 → 慢启动
↓
3个重复ACK → 快重传 → 快恢复 → 拥塞避免
五、流量控制 vs 拥塞控制
| 特性 | 流量控制 | 拥塞控制 |
|---|---|---|
| 目标 | 防止接收方过载 | 防止网络过载 |
| 控制对象 | 发送方与接收方之间 | 发送方与网络之间 |
| 依据信息 | 接收方通告窗口(RWND) | 网络拥塞程度 |
| 实现机制 | 滑动窗口 | 拥塞窗口(CWND) |
| 信号来源 | 接收方ACK | 丢包、重复ACK、RTT变化 |
| 调整变量 | 发送窗口上限 | 拥塞窗口大小 |
TCP的流量控制和拥塞控制是确保网络可靠性和效率的核心机制:
- 流量控制关注接收方能力,通过滑动窗口实现
- 拥塞控制关注网络状态,通过动态调整发送速率实现
两者协同工作,实现公式:
实际发送速率 = min(接收方能力, 网络承载能力)
= min(RWND, CWND)
六、TCP和UDP的对比
1. 核心特性对比
| 特性维度 | TCP(传输控制协议) | UDP(用户数据报协议) |
|---|---|---|
| 连接性 | 面向连接(需要建立、维护、终止连接) | 无连接(直接发送数据) |
| 可靠性 | 可靠传输(确认、重传、排序) | 不可靠传输(不保证送达) |
| 数据传递 | 面向字节流(无消息边界) | 面向数据报(保持消息边界) |
| 传输顺序 | 保证数据顺序到达 | 不保证顺序 |
| 拥塞控制 | 有(慢启动、拥塞避免等) | 无 |
| 流量控制 | 有(滑动窗口) | 无 |
| 头部大小 | 20-60字节(较大) | 8字节(固定,较小) |
| 传输速度 | 相对较慢(因控制机制) | 非常快(开销小) |
| 适用场景 | 需要可靠性的应用 | 实时性要求高的应用 |
2、适用场景详细分析
TCP适合的场景:
- Web浏览(HTTP/HTTPS)
原因:需要完整、有序地传输网页内容
示例:加载包含HTML、CSS、JS、图片的完整页面
- 文件传输(FTP/SFTP)
原因:必须保证文件的完整性和正确性
示例:下载软件安装包、上传备份文件
- 电子邮件(SMTP/IMAP)
原因:邮件内容不能丢失或错乱
示例:发送带附件的商务邮件
- 远程终端(SSH/Telnet)
原因:每个命令和响应必须准确无误
示例:服务器运维、命令行操作
- 数据库访问
原因:保证事务的ACID特性,数据一致性
示例:银行转账、订单提交
UDP适合的场景:
- 实时音视频通信
特点:允许少量丢包,但不能接受高延迟
应用:Zoom/Skype视频通话、在线会议
示例:丢几帧画面可以接受,但卡顿无法忍受
- 在线游戏
特点:需要快速的状态更新
应用:英雄联盟、绝地求生等多人游戏
示例:玩家位置信息需要快速传输,旧位置可丢弃
- DNS查询
特点:查询简单,响应快,重试成本低
流程:客户端→DNS查询→响应
优势:快速,避免TCP握手开销
- 实时流媒体
特点:顺序可能重要,但实时性更重要
应用:直播平台(Twitch、斗鱼)
示例:观众延迟尽可能小,少量丢包可接受
- IoT设备通信
特点:设备资源有限,通信简单
应用:传感器数据上报、智能家居
优势:低功耗、简单实现
- 广播/多播应用
特点:一对多传输,无需连接管理
应用:网络时间协议(NTP)、路由协议
优势:UDP原生支持广播/多播,TCP不支持
3. 性能与特性深度分析
传输效率对比
# 理论有效载荷效率对比
TCP效率 = 数据大小 / (数据大小 + 20~60字节头部 + ACK开销)
UDP效率 = 数据大小 / (数据大小 + 8字节头部)
# 示例:传输1000字节数据
TCP效率 ≈ 1000/(1000+40) ≈ 96% # 假设40字节TCP头
UDP效率 ≈ 1000/(1000+8) ≈ 99.2%
延迟对比分析
小数据传输场景(如DNS查询):
TCP延迟 = 1.5×RTT(握手)+ 1×RTT(数据传输)+ 0.5×RTT(挥手)≈ 3×RTT
UDP延迟 = 1×RTT(直接发送请求并接收响应)
结论:对于短小请求,UDP延迟显著低于TCP
可靠性实现代价
TCP可靠性的代价:
1. 内存开销:维护发送/接收缓冲区、连接状态
2. CPU开销:计算校验和、序列号管理、定时器处理
3. 带宽开销:ACK报文、重传数据
4. 延迟增加:等待ACK、拥塞控制减缓发送速率
UDP的简单性优势:
1. 无连接状态,内存占用小
2. 无复杂逻辑,CPU负担轻
3. 头部小,带宽利用率高
4. 无等待,延迟最低
4. 选择指南
选择TCP的情况(当以下条件重要时):
- 数据完整性是关键需求
- 数据顺序必须保证
- 需要双向通信和会话管理
- 传输的数据量较大
- 网络条件不可靠或变化大
- 应用协议已经基于TCP设计(如HTTP)
选择UDP的情况(当以下条件重要时):
- 低延迟是首要目标
- 可以容忍少量数据丢失
- 一对多或广播通信
- 资源受限环境(嵌入式设备)
- 需要简单快速的请求-响应
- 应用层已实现可靠性机制
高级决策矩阵
| 考虑因素 | 权重高选TCP | 权重高选UDP |
|---|---|---|
| 网络可靠性 | 低可靠性网络 | 高可靠性网络 |
| 延迟要求 | 可接受>100ms | 要求<50ms |
| 数据重要性 | 关键数据 | 非关键/实时数据 |
| 数据大小 | 大数据流 | 小数据包 |
| 开发复杂度 | 愿意处理可靠性 | 追求简单实现 |
| 移动网络 | 可接受连接迁移开销 | 需要快速恢复 |