传输控制协议TCP
目录
传输控制协议TCP概述
TCP的基本定义
TCP的主要特点
TCP的连接
可靠传输的工作原理
停止等待协议
确认丢失与确认迟到
信道利用率与停等协议效率
连续ARQ协议
TCP报文段的首部格式
六个经典控制位(Flags)及语义
窗口(流量控制)
TCP 协议首部选项
TCP可靠传输的实现
以字节为单位的滑动窗口机制
发送窗口的动态变化
接收窗口与发送窗口的关系
TCP 的缓冲区与窗口关系
超时重传时间 RTO
TCP 的自适应算法
选择确认SACK
TCP的流量控制
滑动窗口机制实现流量控制
零窗口与持续计时器
TCP 的传输效率
TCP 的拥塞控制
拥塞控制的一般原理
TCP 的拥塞控制方法
工作流程详解
AIMD 原则
主动队列管理
TCP 的运输连接管理
TCP 的连接建立(三次握手)
TCP 的连接释放(四次挥手)
传输控制协议TCP概述
TCP的基本定义
TCP(Transmission Control Protocol,传输控制协议)是面向连接的、可靠的、基于字节流的传输层协议。
它是TCP/IP体系中最复杂、最核心的协议之一,为应用程序提供端到端的可靠通信服务。
TCP工作在传输层,利用IP提供的尽力而为(best-effort)传输服务,在此基础上通过各种机制(如确认、重传、校验、流量控制等)来保证数据可靠到达。
TCP的主要特点
(1) 面向连接
TCP是面向连接(Connection-Oriented)的传输协议。
通信前,双方必须先建立TCP连接(通过“三次握手”实现)
数据传输结束后,必须释放连接(通过“四次挥手”实现)
这意味着:应用程序在使用TCP之前,必须先有连接的建立过程。
(2) 点对点通信
TCP连接是一对一(Point-to-Point)的,即每条连接只能在两个端点之间建立。
不存在多点广播或多播通信。
(3) 提供可靠的传输服务
TCP保证传输的数据无差错、不丢失、不重复,并且按序到达
这是通过以下机制实现的:
序号机制(Sequence Number)
确认机制(Acknowledgment)
重传机制(Retransmission)
校验和机制(Checksum)
(4) 提供全双工通信
TCP通信双方在连接建立后,都可在任何时刻发送数据。
TCP在两端各维持一个发送缓存和接收缓存,数据在发送端排队等待发送,在接收端暂存等待上层应用处理。
(5) 面向字节流
TCP把应用层交付的数据看作一串无结构的、连续的字节序列(即字节流)
流(stream)指的是数据从一端流入TCP缓冲区,经网络传送后,从接收端TCP缓冲区流出。
TCP并不记录应用层数据的写入边界。发送端应用程序多次写入的数据,可能会在接收端的一次读操作中被全部接收;反之,发送端一次写入的大量数据,也可能被接收端分多次读取。
TCP只保证字节流在接收端被重建后的顺序与发送端一致,但不保留应用层消息的边界。
比如,发送端应用层一次write(100字节),接收端可能分两次read(60+40字节)

TCP的连接
(1) TCP连接的基本抽象
TCP把连接作为最基本的抽象。
连接代表通信双方在传输层上的逻辑通道。
每条TCP连接有两个端点(endpoint),每个端点由IP地址 + 端口号组成,TCP连接的端点叫
作套接字(socket)或插口。
(2) 套接字与连接的关系
根据RFC 793定义:
socket = (IP地址 : 端口号)
一条TCP连接唯一由通信双方的套接字对确定:
TCP连接 ::= {socket1, socket2} = {(IP1:port1), (IP2:port2)}
同一个IP地址可以有多个不同的TCP连接,而同一个端口号也可以出现在多个不同的TCP连接中。
(3) Socket的多重含义
1. 允许应用程序访问连网协议的应用编程接口 API,即运输层和应用层之间的一种接口,称为 socket API,并简称为 socket。
2. 在 socket API 中使用的一个函数名也叫作 socket。
3. 调用 socket 函数的端点称为 socket,如 “创建一个数据报 socket”。
4. 调用 socket 函数时,其返回值称为 socket 描述符,可简称为 socket
5. 在操作系统内核中连网协议的 Berkeley 实现,称为 socket 实现。
上面的这些 socket 的意思都和本文所引用的 RFC 793 定义的 socket(指端口号拼接到 IP 地址)不同。需加以注意。
可靠传输的工作原理
理想情况下,若要实现可靠通信,需满足:
传输过程无差错且接收方按发送顺序正确接收数据。
现实中网络不可靠,因此TCP要在不可靠信道上实现可靠传输。
停止等待协议
(1) 定义
停等协议是一种最简单的可靠传输协议。
发送方每次发送一个分组后必须等待接收方确认(ACK),收到确认后才能继续发送下一个分组。
若超时未收到确认,则重传上一个分组。
(2) 无差错情况(图5-8a)

流程:
发送方A发送M₁;
接收方B收到M₁并发送确认;
A收到确认后发送M₂;
循环往复。
特点:通信过程可靠且按序
(3) 出现差错情况(图5-8b)
若分组在传输中出错或丢失,接收方丢弃该分组且不发确认。
发送方等待超时后重传。
关键机制:
超时定时器:监测分组是否超时。
重传机制:超时重发未确认的分组。
(4) 注意事项
发送方需保留已发送但未确认分组的副本;
分组编号(序号)用于区分新旧分组;
超时设置应略长于平均往返时间RTT。
确认丢失与确认迟到
(1) 确认丢失
若确认分组在传输中丢失,发送方误以为未收到确认而重传。
接收方对重复分组的处理:丢弃重复分组,但仍发送确认。
(2) 确认迟到
若确认分组在网络中延迟到达,发送方会因超时重传同一分组。
接收方收到重复分组同样丢弃并重发确认。
最终系统仍能保持一致性。

信道利用率与停等协议效率
停等协议效率低的原因:
1. 每次只能发送一个分组
2. 必须等待确认才能继续发送
3. 若往返时延RTT较大,则有效传输时间占比极小
信道利用率:



为了提高传输效率,发送方可以不使用低效率的停止等待协议,而是采用流水线传输,即发送方可连续发送多个分组,而不是每次停等等待确认。
当使用流水线传输时,就要使用下面介绍的连续ARQ协议和滑动窗口协议。

连续ARQ协议
连续ARQ协议允许发送方连续发送多个分组,而接收方通过累计确认的方式进行反馈。
机制:
每收到一个确认,发送窗口滑动一格;
未确认的分组保留在发送窗口中;
收到确认后才能释放窗口空间。

图5-12(a)表示发送方维持的发送窗口,它的意义是位于发送窗口内的5个分组都可连续发送出去,而不需要等待对方的确认。这样,信道利用率就提高了。
图5-12(b)表示发送方收到了对第1个分组的确认,于是把发送窗口向前移动一个分组的位置。如果原来已经发送了前5个分组,那么现在就可以发送窗口内的第6个分组了。
累积确认
接收方发送的确认号表示到该序号为止的所有分组均已正确收到。
例如:确认号=5,表示1~5号分组都已收到。
接收方一般都是采用累积确认的方式。这就是说,接收方不必对收到的分组逐个发送确认,而是在收到几个分组后,对按序到达的最后一个分组发送确认,这就表示到这个分组为止的所有分组都已正确收到了。
Go-Back-N(回退N)
累积确认有优点也有缺点。优点是容易实现,即使确认丢失也不必重传;但缺点是不能向发送方及时反映接收方已经正确收到所有分组的信息。
例如,如果发送方发送了前5个分组,而中间的第3个分组丢失了。这时接收方只能对前两个分组发出确认。发送方无法知道后面三个分组的下落,而只好把后面的三个分组都再重传一次。这就叫作Go-back-N(回退N),表示需要再退回来重传已发送过的N个分组。
可见当通信线路质量不好时,连续ARQ协议会带来负面的影响。
TCP报文段的首部格式

TCP 报文段首部总体格式与固定字段(最小 20 字节)
TCP 面向字节流、面向连接、可靠传输。应用数据被封装进报文段(segment)。TCP 首部最小 20 字节,之后是可变长“选项”字段(以 4 字节为单位填充对齐)
源端口 Source Port(16 位)、目的端口 Destination Port(16 位)
标识两端进程的服务接入点,配合四元组 ⟨源 IP, 源端口, 目的 IP, 目的端口⟩ 唯一确定一条 TCP 连接。
序号 Sequence Number(32 位)
TCP 对字节编号,序号字段给出本报文段首字节在连接字节流中的序号。初始序号(ISN)在连接建立时确定。
例:若本段携带 100 字节且序号=301,则数据范围为 [301, 400];下一段若连续发送,则应以 401 作为序号开头。这个“报文段序号”是段内首字节序号,而不是“段号”。
确认号 Acknowledgment Number(32 位)
含义是期望收到对方下一个数据字节的序号,即累计确认。只有当 ACK 标志位=1 时才有效。
例:若确认号=701,表示 0~700 的所有数据字节均已正确接收,“下一个想要的是 701”。
数据偏移 Data Offset(4 位)
指首部长度(以 4 字节为单位),最小值 5(即 20 字节)。有选项时增大,以便定位数据起始处。
保留位(6 位)
预留将来使用,发送端置 0,接收端忽略。
窗口 Window(16 位)
由接收端给出接收窗口大小(又称通告窗口、Advertised Window),单位为字节,表示“从确认号起,接收方当前还能接受的最大数据量”。配合发送端滑动窗口机制实现端到端流量控制。
检验和 Checksum(16 位)
覆盖 TCP 首部与数据,并计算在包含“伪首部”的校验范围上(IPv4/IPv6 的伪首部略有差异)。接收方必须验证,错误即丢弃。
紧急指针 Urgent Pointer(16 位)
当 URG=1 时有效,紧急指针的值是从本报文段首字节的序号开始算起的字节偏移量。用于少量带外/优先数据的快速处理。
选项 Options(可变)+ 填充 Padding
长度必须是 4 的倍数,不足处填充 0。
六个经典控制位(Flags)及语义
ACK:确认标志。连接建立后所有报文段都应置 1(含数据段与纯 ACK 段)。只有 ACK=1 时,确认号字段才有效。
SYN:同步序号,用于建立连接。SYN=1 表示连接请求/同意同步,SYN 段会消耗一个序号(即占用 1 字节序号空间)
FIN:发送方数据发送完毕,要求释放连接。FIN 也消耗一个序号。
RST:复位,表示连接出现严重差错或拒绝非法段/无效端口,立即释放并复位。
PSH:提示“推送”语义,要求接收端尽快把数据推给上层应用(减少缓存等待)
URG:紧急,配合紧急指针字段表明存在紧急数据,接收端应优先交付应用(而不是按普通到达顺序排队)
小结:
① 仅当 ACK=1 时确认号才生效;② SYN/FIN 各自占用 1 个序号
窗口(流量控制)
窗口值是接收端对发送端的“许可”:从“确认号”开始,最多还允许发送多少新数据。这是接收方缓冲区余量的对外暴露。
可发送范围 = [确认号, 确认号 + 窗口值 − 1]
例:确认号=701,窗口=1000 → 发送方当前可继续发送字节区间 [701, 1700] 的数据。
窗口是动态变化的:窗口随接收端处理进度与缓存占用而调整;配合发送端的拥塞控制共同决定实际发送速率。
零窗口与持续计时器:窗口可能通告为 0,发送端需使用“探测报文段”(持续计时器)周期性查询以恢复传输。
TCP 协议首部选项
TCP 首部选项概述
TCP 首部长度可变,最长可达 40 字节。
如果没有使用选项,TCP 首部固定为 20 字节。
选项字段的末尾可能有填充字段,目的是让整个 TCP 首部长度是 4 字节的整数倍。
最大报文段长度(MSS)
MSS 是 TCP 报文段中数据字段的最大长度,不包括 TCP 首部。
设置 MSS 的目的是提高网络传输效率:
如果 MSS 太小,比如只有 1 字节数据,但 IP 数据报总长度可能达到 41 字节(20 字节 IP 首部 + 20 字节 TCP 首部 + 1 字节数据),利用率很低。
如果 MSS 太大,IP 数据报可能需要分片,增加重组和重传的开销。
最佳 MSS 应尽量大,但避免 IP 分片。
默认 MSS 为 536 字节,因此所有主机应能接收的最小 TCP 报文段长度为 556 字节(536 + 20 字节 TCP 首部)
窗口扩大选项
TCP 首部中窗口字段为 16 位,最大窗口为 64 KB。
对于高延迟、高带宽的网络(如卫星网络),64 KB 窗口可能不够用。
窗口扩大选项允许将窗口大小扩大到2^(16+S) -1 ,其中 S 最大为 14,即最大窗口可达2^30 -1
在连接建立时协商,也可通过发送 S=0 恢复为 16 位窗口。
时间戳选项
时间戳选项占 10 字节,包括:
时间戳值字段(4 字节)
时间戳回送回答字段(4 字节)
两个主要功能:
精确计算往返时间(RTT)
防止序号绕回(PAWS)
TCP 序号只有 32 位,在高速网络中可能很快重复。
使用时间戳可区分新旧报文段,避免混淆。
还存在选择确认(SACK) 选项,用于提高重传效率,允许接收方告知发送方哪些数据已收到,避免不必要的重传。
TCP可靠传输的实现
TCP(Transmission Control Protocol,传输控制协议)是一种面向连接的、可靠的、基于字节流的传输层协议。
它通过确认(ACK)机制、超时重传机制、序号机制、流量控制和滑动窗口机制,保证数据在不可靠的 IP 层上可靠地传输
数据传输的方向与窗口
在讲解可靠传输原理时,通常假定数据在一个方向上传输,例如:
A 发送数据;
B 接收并发送确认。
这样可以简化讨论,只需分析 A 的发送窗口与 B 的接收窗口。
以字节为单位的滑动窗口机制
TCP 的滑动窗口以字节(Byte)为最小单位。窗口的作用是限制发送方在未收到确认前可以连续发送的数据量,确保既高效又不会使接收方缓存溢出。
发送窗口的组成(以 A 为发送方)
发送窗口分为四个区域:
1. 已发送并收到确认的数据(不再需要保留)
2. 已发送但未收到确认的数据;
3. 允许发送但尚未发送的数据;
4. 不允许发送的数据(超出接收方通告窗口范围)
窗口的后沿对应最早未确认的字节序号,前沿对应允许发送的最大序号。
随着确认的到达,窗口会不断前移(滑动)
发送窗口的动态变化
1. 窗口前移
当发送方收到新的确认(ACK)时,表示接收方成功接收了一部分数据;
发送方将窗口后沿前移,以便可以继续发送更多数据;
这种现象称为窗口前移。
2. 窗口后移
当接收方的缓存变小时,可能会通告更小的窗口;
此时发送方必须减小可发送范围,这种情况称为窗口后移。
TCP 规定窗口可以收缩,但不鼓励频繁收缩,以免造成混乱。
发送窗口状态的三个指针(P₁、P₂、P₃)
图 5-15 与图 5-16 所示,A 的发送窗口可用三个指针表示:
| 指针 | 含义 |
|---|---|
| P₁ | 已发送并确认的最后一个字节序号(窗口后沿) |
| P₂ | 已发送但尚未确认的最后一个字节序号 |
| P₃ | 允许发送的最大字节序号(窗口前沿) |
窗口的三个区域
P₁ ~ P₂:已发送但未确认(灰色部分)
P₂ ~ P₃:允许发送但尚未发送(可用窗口)
P₃ 之后:不允许发送(超出接收方窗口)

初始状态(图 5-14)
B 告诉 A:“我期望收到序号 31,我的接收窗口是 20”
A 的发送窗口就是 31~50(共20字节)

A 发送部分数据(图 5-15)
A 发送了 31~41(共11字节)
此时:
已发送未确认:31~41
可用窗口:42~50(9字节)

B 确认部分数据(图 5-16)
B 收到 31~33,交付主机,窗口前移
B 发送确认号 34,窗口仍为 20
A 收到后,窗口前移,可用窗口变为 42~53

窗口满(图 5-17)
如果 A 发送了窗口内所有数据(34~53),则可用窗口为 0,必须等待 B 的确认才能继续发送。
接收窗口与发送窗口的关系
接收窗口的定义
接收方 B 维护一个接收窗口,用于存放:
1. 已收到但未被上层应用读取的数据;
2. 尚未收到但可接收的数据空间。
接收窗口的大小取决于应用层读取数据的速度与接收缓存大小。
发送窗口与接收窗口的关系
二者的关系可以简述为:
发送窗口 ≤ 接收窗口通告值
发送方依据接收方通告的窗口大小调整自己的发送窗口,以实现流量控制。
TCP 的缓冲区与窗口关系

1. 发送方(图 5-18(a))
发送缓存存放:
应用程序传过来、但还没发送的数据;
已发送但还没收到确认的数据。
发送窗口是发送缓存的一部分,表示“允许发送但还没确认的数据范围”
发送缓存的后沿与发送窗口的后沿重合(即已确认的数据会被移除)
应用程序写入速度不能太快,否则会撑满缓存
2. 接收方(图 5-18(b))
接收缓存存放:
已按序到达但还没被应用程序读取的数据
未按序到达但已被接收的数据(暂存等待补齐)
接收窗口表示还能接收多少数据,取决于应用程序读取速度
如果应用程序读取慢,接收窗口会变小,甚至为0(通知发送方停止发送)
超时重传时间 RTO
为什么这是个复杂问题?
网络环境多变:报文段可能经过高速局域网,也可能经过多个低速网络
RTO设置过短 → 不必要的重传 → 网络负荷增大
RTO设置过长 → 空闲时间增加 → 传输效率降低
TCP 的自适应算法
核心概念:RTT(往返时间)
记录报文段发出时间和收到确认时间的时间差
解决方案:自适应算法
TCP 不采用固定超时时间,而是根据网络状况动态调整。它通过测量往返时间(RTT)即数据包发出到收到确认的时间差来估算 RTO。
关键计算步骤:
计算平滑的 RTT(RTTs):
用一个公式对多次测量的 RTT 进行加权平均,使结果更稳定。
公式:新的 RTTs = (1 - α) * 旧的 RTTs + α * 新的 RTT 样本
α 是权重系数(推荐值 0.125):
α 小 → RTTs 对新的 RTT 样本不敏感,变化平缓。
α 大 → RTTs 对新的 RTT 样本敏感,变化迅速。
计算 RTT 的偏差(RTTd):
衡量 RTT 的波动程度。RTT 越不稳定,偏差越大,需要的 RTO 也应越大。
公式:新的 RTTd = (1 - β) * 旧的 RTTd + β * |RTTs - 新的 RTT 样本|
β 是另一个权重系数(推荐值 0.25)
最终计算 RTO:
公式:RTO = RTTs + 4 * RTTd
这样,RTO 既考虑了平均往返时间,又为可能的波动预留了缓冲。
核心问题:重传二义性
场景:发送方发送一个报文段,超时后重传,然后收到了一个确认(ACK)
难题:无法判断这个ACK是针对第一次发送的报文段,还是针对重传的报文段。
错误判断的后果:
如果把重传的ACK误认为是第一次的:会测得一个极长的RTT(从第一次发送到收到ACK),导致计算出的RTO偏大,网络响应变慢。
如果把第一次的ACK误认为是重传的:会测得一个极短的RTT(从重传到收到ACK),导致计算出的RTO偏小,引发更多不必要的重传,恶性循环。

解决方案:Karn 算法
Karn提出了一个简单而有效的核心原则:
只要报文段发生了重传,就忽略这次传输的RTT样本,不将其用于更新加权平均RTTs。
优点:
从根本上避免了“重传二义性”问题,防止RTO计算被无效样本污染。
带来的新问题:
如果是因为网络延迟真正变大(而非临时波动)导致的超时重传,由于忽略了重传样本,RTO将无法根据新的网络状况进行更新,会一直保持在过小的值,导致后续报文段持续重传。
对Karn算法的修正:指数退避
为了解决上述新问题,对Karn算法进行了补充:
策略:报文段每重传一次,就将RTO增大一倍(即 新的RTO = 旧的RTO * 2)。这被称为指数退避策略。
逻辑:一旦发生重传,就认为网络可能出现拥塞或延迟增大,因此主动地、大幅度地增加超时时间,给网络更多时间恢复。
恢复:只有当报文段未经重传就成功确认时,才重新使用标准的公式(RTO = RTTs + 4 * RTTd)来动态计算RTO
选择确认SACK
1. 它要解决什么问题?
在标准的TCP确认机制中,接收方只能确认连续收到的最大序号。比如,它收到了字节1-1000和1501-3000,但中间缺了1001-1500。它只能发送确认号1001,意思是“我期望收到从1001开始的字节”。

这会导致:
发送方只知道1001之后的数据都没收到,于是会重传从1001开始的所有数据(即1001及之后的所有字节)
但实际上,1501-3000这段数据接收方已经正确收到了,重传它们造成了网络带宽的浪费。
2. 解决方案:选择确认(SACK)
SACK机制允许接收方告诉发送方:“我虽然缺了一些数据,但我也收到了一些不连续的数据块。请你只重传我缺少的那部分,别重传我已经有的。”
工作原理:
接收方在TCP报头的一个可选字段里,附带自己收到的不连续数据块的边界信息。
如图5-20所示,每个数据块用 左边界(L) 和 右边界(R) 来标识。
关键点:边界标识的是左闭右开区间 [L, R)。例如 [1501, 3001) 表示收到了字节1501到3000。
3. 技术细节与限制
协商机制:在建立TCP连接时,双方必须通过选项告知对方“我支持SACK”,否则无法使用。
空间限制:TCP报头的选项字段最多只有40字节。
报告一个数据块需要8字节(左右边界各4字节)。
加上选项类型和长度字段,最多只能报告4个不连续的数据块。
确认号不变:即使使用了SACK,原来标准的确认号字段依然有效,它仍然指示期望收到的下一个字节序号。
TCP的流量控制
定义
流量控制(Flow Control) 是一种防止发送方过快发送数据、以致接收方来不及接收的机制。
它的核心目标是控制发送方的发送速率,使其不超过接收方的接收处理能力。
TCP 是一种 全双工(full-duplex) 的协议,每一端都维护一个称为接收窗口(Receiver Window, rwnd) 的变量,用于表示自己当前还能接收多少数据。
滑动窗口机制实现流量控制
TCP 的流量控制是通过滑动窗口(Sliding Window) 实现的。
滑动窗口机制允许发送方在未收到确认(ACK)的情况下连续发送多个字节的数据,但总量不能超过接收方声明的窗口大小 rwnd
举例说明(图 5-21)
设主机 A 向主机 B 发送数据,B 告诉 A:
我的接收窗口是 400 字节(rwnd = 400)
这表示 A 最多只能发送 400 字节未被确认的数据。

此时,A 必须停止发送,直到 B 告诉它新的窗口大小。
零窗口与持续计时器
当 B 的窗口为 0 时,A 无法继续发送。
但 TCP 必须防止一种死锁情况:
B 释放了缓冲空间,发送了新的 rwnd;
但 ACK 报文丢失,A 一直等待窗口恢复。
为避免这种情况,TCP 在每个连接中设置一个持续计时器
当定时器到期后,如果窗口仍为 0,发送方会发送一个探测报文段(只含 1 字节数据),以请求接收方重新报告最新窗口值。
若仍为 0,则继续等待;若窗口大于 0,恢复正常发送。
TCP 的传输效率
1. 报文段大小控制(MSS)
TCP 为了提高效率,会尽量发送较大的报文段。
常见的两种发送策略:
定时发送策略:当缓存数据达到最大报文段长度(MSS)时发送。
定时器触发策略:即使数据未满 MSS,但定时器到期,也发送当前缓冲的数据。
2. Nagle 算法 (防止小报文过多)
问题:如果每次输入 1 个字符就立刻发送一个 TCP 报文,会造成严重的网络负担(如 TELNET)
每个小报文都要加上 TCP 首部(20 字节)+ IP 首部(20 字节),总共 40 字节开销。
解决方案:
采用 Nagle 算法:
当发送方已发送一个未被确认的报文时,暂缓发送新的小报文,等待确认或积累足够数据再发。
其核心规则:
若已发送的数据未确认,则新的小数据先缓存在缓冲区;
一旦收到 ACK 或积累到 MSS 大小的数据,就发送新的报文。
效果:减少小报文数量,显著提高传输效率。
3. 糊涂窗口综合征(Silly Window Syndrome)
(1) 定义
糊涂窗口综合征(Silly Window Syndrome, SWS) 指的是:
发送方和接收方频繁地以非常小的窗口或报文段传输数据,导致效率极低。
两种情况:
接收方引起:接收缓冲区逐渐腾出少量空间,就立即通告一个很小的窗口。
发送方引起:每有少量数据生成就立刻发送一个小报文。
(2) 解决方法
为防止 SWS,TCP 规定:
接收方:
不要在接收缓冲刚释放出一点点空间时就通告窗口;
应等待足够空间(如缓冲区一半或达到 MSS)再通告新的窗口大小。
发送方:
不要发送太小的报文段,应积累数据直到:
1. 报文长度达到 MSS;
2. 或接收窗口有一半的空闲;
3. 或等待一定时间(防止过久不发)
TCP 的拥塞控制
拥塞控制的一般原理
1. 拥塞的定义
在计算机网络中,链路带宽、交换节点缓存与处理能力等都是有限资源。
当网络中对这些资源的需求超过可用资源的供给能力时,网络性能开始恶化,这种情况称为拥塞(congestion)
可用资源的供给能力指的是网络系统中各种关键资源在单位时间内能够处理数据的最大容量或速率
公式表达为:
![]()
也就是说,当网络负载(offered load)持续增加,而网络能提供的吞吐量无法同步增长时,就发生了拥塞。
2. 拥塞的形成原因
拥塞并非单一原因导致,而是多种因素共同作用:
当中间节点(如路由器)的缓存被占满;
节点的处理速度不足;
各节点间速率不匹配;
或者数据包在多个节点排队等待时间过长。
例如:
当某个节点的缓存区满时,新到的数据包必须丢弃,从而造成重传,加重网络负担,形成恶性循环,网络性能显著下降。
3. 拥塞与流量控制的区别
| 对比项目 | 拥塞控制 | 流量控制 |
|---|---|---|
| 控制对象 | 整个网络 | 点对点连接(发送端↔接收端) |
| 控制目的 | 防止网络过载、资源枯竭 | 防止接收端缓存溢出 |
| 控制方式 | 调整发送速率,使网络整体平稳 | 调整窗口大小,匹配接收能力 |
| 作用范围 | 全局(路由器、链路、主机) | 局部(发送端与接收端) |
类比图:

流量控制和拥塞控制的区别可以用图 5-22 的简单比喻来说明。图中表示一水龙头通过管道向一个水桶放水。图 5-22 (a) 表示水桶太小,来不及接收注入水桶的水。这时只好请求管水龙头的人把水龙头拧小些,以减缓放水的速率。这就相当于流量控制。图 5-22 (b) 表示虽然水桶足够大,但管道中有很狭窄的地方,使得管道不通畅,水流被堵塞。这种情况被反馈到管水龙头的人,请求把水龙头拧小些,以减缓放水的速率,为的是减缓水管的堵塞状态。这就相当于拥塞控制。请注意,同样是把水龙头拧小些,但目的是很不一样的。
4. 拥塞的图表化理解(图 5-23)
图5-23 定义了拥塞的各个阶段
横轴(X轴):提供的负载 - 即网络需要处理的流量总量。
纵轴(Y轴):吞吐量 - 即网络成功输出的流量总量。
图表中描绘了两种曲线:
理想情况(45°斜线 + 水平线):
在达到网络最大处理能力之前,吞吐量等于提供的负载(来多少,过多少)
达到上限后,吞吐量保持稳定,不再增长,多余的负载被丢弃。网络始终工作在最佳状态。
实际情况(S型曲线,最终下降):
轻度拥塞:在达到理想上限之前,吞吐量的增长就开始变慢。这意味着已经有分组被丢弃,网络效率已经开始降低。
拥塞:当负载继续增加,吞吐量不增反降。这是因为大量的重传和排队延迟严重恶化了网络性能。
死锁:负载大到一定程度,吞吐量降为零,网络完全瘫痪。
拥塞的征兆(如分组丢失)出现得比我们想象的要早,并且拥塞本身会自我恶化,形成恶性循环。

5. 拥塞控制的根本思路与挑战
根本思路:解决拥塞无非是让“需求”不超过“供给”。
增加供给:增加带宽、链路、缓存等资源。
减少需求:拒绝新连接、要求用户降低发送速率等。
设计难点(为什么很难):
1. 动态性问题:网络流量是瞬息万变的,不是一个静态模型。
高速网络的挑战:缓存增长的速度跟不上链路速度的增长,更容易因缓存不足而丢包。
2. 因果关系的复杂性:分组丢失是拥塞的“征兆”而非“原因”。有时,拥塞控制机制本身(如激进的重传)就是导致性能恶化甚至死锁的原因。这是一个非常重要的观点。
3. 反馈控制的平衡:下文提到的闭环控制需要精妙的平衡。
6. 拥塞控制的方法论:开环 vs 闭环
这是从控制理论角度提出的两种根本性方法:
开环控制
思想:防患于未然。在系统设计时,就通过精心规划(如预留资源、制定数据流规则)来预防拥塞的发生。
特点:系统运行后,策略是固定的,不根据当前状况进行调整。
闭环控制
思想:基于反馈。这是一个动态调整的过程,包含三个步骤,形成一个“闭环”:
1. 监测:检测网络是否发生拥塞。指标包括:丢包率、平均队列长度、超时重传数、分组延迟及其波动等。
2. 通知:将拥塞信息传递到可以采取行动的源头(如发送数据的主机)。通知方式可以是:
由路由器向源站发送一个警告信息。
在数据分组中预留一个拥塞指示比特/字段
发送探测分组询问。
3. 调整:源站根据通知采取行动,降低发送速率以缓解拥塞。
核心挑战:
时机:行动太快会导致网络震荡;行动太慢则无法缓解拥塞。
开销:反馈信息本身也会消耗网络资源,可能加剧拥塞。
TCP 的拥塞控制方法
TCP 拥塞控制机制包括四种核心算法(RFC 5681):
慢开始(Slow Start)
拥塞避免(Congestion Avoidance)
快重传(Fast Retransmit)
快恢复(Fast Recovery)
1. 慢开始
目的:在连接刚建立时,以一种试探性的方式快速找到当前网络的最佳承载能力。它并不慢,而是指起点很低。
工作原理:
1. 初始时,设置拥塞窗口 cwnd 为一个很小的值(如 1-4 个报文段)
2. 每收到一个对新数据的确认,就将 cwnd 增加 1
3. 这会导致指数级增长:在一个往返时延内,如果所有报文都被确认,cwnd 会翻倍(1 -> 2 -> 4 -> 8 ...)
为什么需要它:如果一开始就发送大量数据,很可能直接冲垮网络。慢开始通过指数增长,能迅速逼近网络的容量上限。
假设初始拥塞窗口 cwnd = 1(即一次能发送 1 个报文段),且网络无丢包,所有报文都能被确认:
第 1 个 RTT 开始:发送 1 个报文段(记为 A)
收到 A 的确认后,cwnd 增加 1 → cwnd = 2
第 2 个 RTT 开始:此时 cwnd = 2,可以一次发送 2 个报文段(B 和 C)
收到 B 的确认后,cwnd 增加 1 → cwnd = 3
收到 C 的确认后,cwnd 再增加 1 → cwnd = 4
一个 RTT 内,cwnd 从 2 变成 4(翻倍)
第 3 个 RTT 开始:cwnd = 4,一次发送 4 个报文段(D、E、F、G)
每个报文的确认到达后,cwnd 分别增加 1,共增加 4 次 → cwnd 从 4 变成 8(翻倍)

2. 拥塞避免
目的:当拥塞窗口增长到一定程度后,为了防止其增长过快导致拥塞,需要从指数增长切换为线性增长。
触发条件:当 cwnd 增长到慢开始门限时。
慢开始门限 ssthresh :一个关键的状态变量,用来区分使用慢开始算法还是拥塞避免算法。
工作原理:
1. 当 cwnd >= ssthresh 时,进入拥塞避免阶段
2. 改为每经过一个往返时延,才将 cwnd 加 1
3. 这导致线性增长,增长速度比慢开始阶段平缓得多
3. 快重传
目的:解决个别报文段丢失被误判为网络拥塞的问题。如果没有快重传,发送方必须等待超时才会重传,效率低下。
工作原理:
1. 接收方如果收到一个失序的报文段,应立即重复发送前一个按序报文段的确认。
2. 例如,发送方发送了 M1, M2, M3, M4... 接收方收到了 M1, M2, M4。当收到 M4(失序)时,接收方不是等待,而是立即再发一个对 M2 的确认。
3. 当发送方连续收到 3 个重复的确认时,它就推断出某个中间的报文段(如 M3)丢失了,无需等待超时,立即重传该丢失的报文段。

4. 快恢复
目的:与快重传配合使用。当通过快重传知道只是个别报文丢失(而非严重拥塞)时,不执行激进的慢开始,而是温和地降低发送速率。
工作原理:
1. 当发送方收到 3 个重复确认,并执行快重传后:
将 ssthresh 设置为当前 cwnd 的一半:ssthresh = cwnd / 2 (乘性减少)
不将 cwnd 设为 1,而是设为新的 ssthresh 值(或 ssthresh + 3)
2. 然后直接进入拥塞避免阶段,继续线性增长
当出现 3 个重复 ACK(说明仅个别报文丢失,网络轻微拥塞),触发快恢复,而不采用超时重传那种 cwnd 归 1、重新慢开始的激进操作

工作流程详解
结合图5-25 来理解整个过程:

1. 初始阶段:
连接刚建立,设置 cwnd = 1,ssthresh = 16
使用慢开始算法,cwnd 指数增长(1 -> 2 -> 4 -> 8 -> 16)
2. 首次切换:
当 cwnd 增长到 ssthresh = 16 时,切换为拥塞避免算法
cwnd 开始线性增长(17 -> 18 -> 19 ...)
3. 发生超时(判断为拥塞):
当 cwnd 增长到 24 时,发生了超时。这表明网络可能发生了比较严重的拥塞(路由器丢包)
TCP 采取激进措施:
将 ssthresh 降为当前 cwnd 的一半:ssthresh = 24 / 2 = 12
将 cwnd 重置为 1
重新开始慢开始过程。
4. 遇到快重传/快恢复:
当 cwnd 再次通过慢开始增长到新的 ssthresh = 12 时,进入拥塞避免。
当 cwnd = 16 时,发送方收到了 3 个重复确认。这触发了快重传机制。
发送方立即重传丢失的报文,并进入快恢复:
设置 ssthresh = cwnd / 2 = 8
设置 cwnd = ssthresh = 8(或 ssthresh + 3)
然后直接进入拥塞避免阶段,继续线性增长
AIMD 原则
整个过程体现了 AIMD 原则,这是 TCP 拥塞控制的核心思想:
加法增大:在拥塞避免阶段,cwnd 线性加 1 增长。探测网络还有多少可用带宽。
乘法减小:一旦发生拥塞(超时或重复ACK),就将 ssthresh 减半。迅速减小数据注入量,以缓解网络压力。
这个增大-拥塞-减小-再增大的循环,形成了图 5-25 中典型的锯齿形波形,这也是 TCP 动态适应网络变化的直观体现。
最终发送窗口
发送方的实际发送窗口不仅受拥塞窗口限制,还受接收方接收能力限制。所以最终窗口是:
发送窗口上限 = Min(接收方窗口 rwnd, 拥塞窗口 cwnd)
如果 rwnd 小,说明是接收方处理能力不足。
如果 cwnd 小,说明是网络传输路径拥塞。
主动队列管理
1. 问题背景
传统路由器采用尾部丢弃策略(Tail-Drop Policy):
队列满时,所有新到分组直接丢弃 → 导致全局同步现象:
多个 TCP 连接同时检测到丢包、同时减速、同时重启慢开始,导致网络吞吐剧烈波动。
2. AQM 的思想
主动队列管理(AQM, Active Queue Management)的目标是在队列满之前就主动丢弃部分分组,从而提前警告 TCP 发送方减速,防止拥塞。
3. 随机早期检测(RED, Random Early Detection)
1998 年提出,RED 是最经典的 AQM 算法。
原理:
设置最小阈值(minth)与最大阈值(maxth)
当队列平均长度在两者之间时,以概率 p 随机丢弃分组;
当长度超过最大阈值时,所有新分组直接丢弃;
当长度低于最小阈值时,不丢弃。
RED 实现了:
提前丢弃,预防拥塞
概率丢弃,避免同步
4. RED 的意义与改进
RED 能减少大规模 TCP 同步,提升网络平稳性;
但实际部署效果不稳定,取决于参数配置;
后续提出多种改进算法,如:
WRED(加权 RED)
ARED(自适应 RED)
CoDel(Controlled Delay)
PIE(Proportional Integral Controller Enhanced)
这些算法已成为现代智能路由器的拥塞控制核心机制。
TCP 的运输连接管理
连接管理的意义与目标
TCP 是一种面向连接(connection-oriented)的传输层协议。
所谓连接,是指通信双方在数据传输前必须协商通信参数、建立可靠连接的逻辑通路。
TCP 通信的整个生命周期分为三个阶段:
连接建立
数据传输
连接释放
连接管理的核心任务是保证:
每一条连接的建立和释放都可靠有序;
各方资源(缓存、端口号、控制块等)合理分配与回收;
防止因延迟报文等造成的错误连接。
TCP 的连接建立(三次握手)
1. 建立连接的必要条件
TCP 在建立连接时,必须解决三个基本问题:
双方都能确认对方的存在(活跃检测)
交换必要的初始参数(如初始序号 ISN、窗口大小、是否支持扩展选项等)
为传输数据预留资源(缓存区、TCB 控制块、端口号等)
2. 客户端与服务器的角色
客户端(Client):主动发起连接请求的一方;
服务器(Server):被动等待连接请求的一方。
两者的 TCP 进程都处于最初的 CLOSED(关闭) 状态。
3. 三次握手的过程(图 5-28)

(1)第一次握手
客户端 A 发送一个 SYN 报文段:
SYN=1:表示请求建立连接
seq=x:客户端随机选择一个初始序列号
同时创建传输控制块 TCB(Transmission Control Block),进入SYN-SENT(同步已发送)状态
SYN 报文段只用于建立连接,不能携带数据,但会消耗一个序号
(2)第二次握手
服务器 B 收到 SYN 后,创建自己的 TCB,并回复:
SYN=1:表示服务器也请求建立连接
ACK=1:表示确认客户端的序列号
ack=x+1:确认号,表示期望收到客户端的下一个序列号
seq=y:服务器随机选择一个初始序列号
表示“收到你的请求,我同意建立连接”
B 状态变为 SYN-RCVD(同步已接收)
此时,B 既确认了 A 的 SYN,也发送了自己的 SYN 请求,完成了双向同步的一半
(3)第三次握手
客户端 A 收到 B 的 SYN+ACK 报文后,返回:
ACK=1:表示确认
ack=y+1:确认号,表示期望收到服务器的下一个序列号
seq=x+1:客户端序列号(因为第一次握手消耗了一个序号)
该报文可以携带数据,也可不携带
A 状态变为 ESTABLISHED(已建立连接)
B 收到该确认后,也进入 ESTABLISHED 状态
4. 为什么需要三次握手而不是两次?
如果只进行两次握手,可能出现已失效的连接请求报文段被误接收
例如:
A 向 B 发送的第一个 SYN 报文在网络中长时间滞留;
A 因未收到确认而重传 SYN 并成功建立连接;
此时旧的 SYN 报文又到达 B,B 误以为 A 要建立新连接,从而开启错误连接;
结果产生冗余连接,浪费资源。
三次握手通过确认 ACK的确认机制,确保双方对连接建立的意愿都是最新的、有效的。
TCP 的连接释放(四次挥手)
1. 连接释放的目的
在数据传输结束后,TCP 必须可靠地释放连接,确保:
双方都完成所有数据的发送;
通信资源(端口、缓存、TCB)都被正确回收;
避免半开连接或资源泄漏。
2. 四次挥手的过程(图 5-29)

(1)第一次挥手(FIN)
客户端发送一个TCP报文段,其中:
FIN=1:表示请求释放连接
seq=u:客户端序列号
客户端进入 FIN-WAIT-1 状态(终止等待 1)
(2)第二次挥手(ACK)
服务器收到FIN后,发送一个确认报文段,其中:
ACK=1:表示确认
ack=u+1:确认号
seq=v:服务器序列号
服务器进入 CLOSE-WAIT 状态(关闭等待)
客户端收到确认后,进入 FIN-WAIT-2 状态(终止等待 2)
此时,从客户端到服务器的连接已关闭,但服务器到客户端的连接仍可传输数据(半关闭状态)
(3)第三次挥手(FIN)
服务器发送一个FIN报文段,其中:
FIN=1:表示服务器也请求释放连接
ACK=1:确认
ack=u+1:确认号(与第二次挥手相同)
seq=w:服务器序列号(可能因发送数据而增加)
服务器进入 LAST-ACK 状态(最后确认)
(4)第四次挥手(ACK)
客户端收到FIN后,发送一个确认报文段,其中:
ACK=1:表示确认
ack=w+1:确认号
seq=u+1:客户端序列号(因为第一次挥手消耗了一个序号)
客户端进入 TIME-WAIT 状态(时间等待),等待 2 * MSL(最长报文段寿命)时间后进入 CLOSED 状态。
服务器收到确认后,立即进入 CLOSED 状态。
MSL(Maximum Segment Lifetime)定义为报文在网络中存活的最长时间,通常为 2 分钟。
第二次挥手说的连接已经关闭指的是不能发送应用层数据了
3. 为什么要等待 2MSL?
有两个原因:
1. 确保最后的 ACK 能被对方接收
若该 ACK 丢失,对方会重发 FIN
A 在等待期间可重新发送 ACK
2. 防止旧连接报文干扰新连接
若立即关闭,新旧连接共用端口号,可能收到延迟报文而造成混乱。
