TCP 协议浅析

18 阅读11分钟

一、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 状态

为什么需要三次握手?

  1. 防止历史连接:避免旧的重复连接请求造成混乱
  2. 同步序列号:双方交换初始序列号,确保可靠传输
  3. 验证双方能力:确认双方的发送和接收能力都正常

二、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 状态

为什么需要四次挥手?

  1. TCP是全双工的:每个方向都需要单独关闭
  2. 半关闭状态:允许一方在发送完数据后关闭发送通道,但仍可接收数据
  3. 确保数据完整性:保证所有数据都被正确处理

TIME_WAIT状态的作用

  1. 可靠终止:确保最后的ACK能被对方收到
  2. 避免旧连接干扰:让所有报文在网络中消失,防止被新连接误接收

三、流量控制(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,发送方停止发送
死锁风险:接收方后续发送的非零窗口更新可能丢失

解决方案

  1. 零窗口探测

    • 发送方定时(如5-60秒)发送1字节的探测报文
    • 接收方用当前窗口大小响应
    • 确认接收方是否恢复接收能力
  2. 持续定时器

    • 发送方维护一个定时器
    • 窗口为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的情况(当以下条件重要时):

  1. 数据完整性是关键需求
  2. 数据顺序必须保证
  3. 需要双向通信和会话管理
  4. 传输的数据量较大
  5. 网络条件不可靠或变化大
  6. 应用协议已经基于TCP设计(如HTTP)

选择UDP的情况(当以下条件重要时):

  1. 低延迟是首要目标
  2. 可以容忍少量数据丢失
  3. 一对多或广播通信
  4. 资源受限环境(嵌入式设备)
  5. 需要简单快速的请求-响应
  6. 应用层已实现可靠性机制

高级决策矩阵

考虑因素权重高选TCP权重高选UDP
网络可靠性低可靠性网络高可靠性网络
延迟要求可接受>100ms要求<50ms
数据重要性关键数据非关键/实时数据
数据大小大数据流小数据包
开发复杂度愿意处理可靠性追求简单实现
移动网络可接受连接迁移开销需要快速恢复