传输层为不同主机上的应用进程提供了端到端的逻辑通信,中间系统不涉及传输层的操作。
一般而言,网络层负责主机间的通信,传输层负责进程间的通信。
互联网应用的两大传输协议:
- TCP:面向连接、可靠按序传输、拥塞/流量控制、连接建立
- UDP:无连接、尽力而为服务、无拥塞控制、头部开销小
多路复用与解复用
基本定义
- 多路复用:发送方收集多个套接字数据,添加传输层头部后发送给网络层。
- 解复用:接收方根据头部信息,将数据点分发给正确的套接字。
具体实现
依靠 源/目的IP地址 + 源/目的端口号 确定进程和套接字,两种协议的需求不尽相同:
- UDP(无连接解复用):仅通过目的端口号分发,不同源IP/端口可能指向同一个套接字。
- TCP(面向连接解复用):通过四元组(源 IP、源端口、目的 IP、目的端口)分发,每个连接对应独立套接字。
UDP传输协议
UDP协议的特点:
- 极简协议,无连接、无握手、无连接状态。
- 尽力而为服务,数据可能丢失、乱序。
- 头部仅 8 字节,开销极小
- 无拥塞控制,可按应用需求全速发送
使用UDP协议的应用:DNS,SNMP,实时流媒体,HTTP/3 ……
特别地,UDP不保证可靠交付,但这并不意味着应用对数据的要求是不可靠的,所有维护可靠性的工作可由用户在应用层实现。
例如:HTTP/3 就是基于 QUIC 在应用层实现可靠传输的协议。
UDP报文结构
包含源端口、目的端口、长度、校验和。
| 字段 | 内容 |
|---|---|
| 源端口 | 占用2个字节(16位),标识发送方应用程序的端口号 |
| 目的端口 | 占用2个字节(16位),标识接收方应用程序的端口号 |
| 长度 | 占用2个字节(16位),标识UDP数据报的总长度,包括首部和数据。因此,最小长度为8字节 |
| 校验和 | 占用2个字节(16位),用于检测UDP数据报在传输过程中是否受到损坏 |

使用 UDP 请求服务至少需要一个往返时间 $\text{RTT}$。
UDP校验
UDP校验和(checksum)可以检测数据在传输过程中是否发生错误。
UDP的校验和的检测能力并不强,但好处是简单、迅速。
发送端
UDP的发送方需要计算checksum字段的值并填充进UDP报文。
| 步骤 | 操作 |
|---|---|
| 构造伪头部 | 伪头部包括:源目的IP地址(32位)、目的IP地址(32位)、协议(8位,UDP为17)、UDP长度(16位,头部+数据总字节数)、填充位(8位,通常为0,确保伪头部的总长度为12字节) |
| 组合校验数据 | 将以下内容按16位一组分组:伪头部、UDP头部(包括源端口、目的端口、长度、校验和字段,校验和字段初始置 0)、数据部分 |
| 计算16位和 | 将所有16位数逐个相加。如果有进位,将进位取出并加到低16位,称作回卷 |
| 按位取反 | 对最终的16位和按位取反得到校验和,并将该值填入 UDP 头部的校验和字段中 |
备注回卷操作示例:若两个16位数相加得
1 0000 0000 0000 0001,则取低16位0000 0000 0000 0001并加1,得0000 0000 0000 0010。UDP 校验和是可选的,若不使用,校验和字段置为全
0。
接收端
| 步骤 | 操作 |
|---|---|
| 提取校验数据 | 接收端同样构造伪头部(使用接收到的 IP 头部信息),并提取伪首部、UDP首部(包括接收到的校验和)、数据部分 |
| 计算16位和 | 将所有16位数(包括接收到的校验和)相加,记录并回卷进位。如果数据无误,和的结果应为1111 1111 1111 1111(全 1) |
| 验证结果 | 如果最终和为全1,说明数据正确,无错误。如果和不为全 1,说明数据在传输中发生错误,接收端通常丢弃该数据报(UDP 不负责重传) |
备注如果接收到校验和为全
0,表示发送端禁用了校验和,接收端可直接接受数据(不校验)。如果校验和为全
1,需按上述步骤验证。
TCP传输协议
TCP传输协议的特点:
- 面向连接:需要三次握手和四次挥手。
- 可靠交付:使用 ACK 和重传机制。
- 面向字节流:具有滑动窗口。
- 全双工:连接的双方均可以发送和接收信息。
TCP报文结构
| 字段 | 内容 |
|---|---|
| 源端口号 | 16位,标识发送端的端口号 |
| 目的端口号 | 16位,标识接收端的端口号 |
| 序列号 | 32位,标识TCP报文段中第一个数据字节的序列号,用于实现 TCP 的可靠性机制 |
| 确认号 | 32位,如果设置了 ACK 标志位,该字段包含了期望接收的下一个数据字节的序列号。用于确认已经成功接收的数据 |
| 首部长度 | 4位,标识TCP报文的首部长度,单位为32字 |
| 保留位 | 6位,保留以供之后使用,默认设置为0 |
| 标志位 | TCP的6个标志位 |
| 窗口大小 | 16位,标识发送端的可用接收窗口的大小;接收端可以根据这个字段的值来告诉发送端可以发送多少数据而不会导致拥塞 |
| 校验和 | 16位,检测TCP首部和数据部分的传输中的错误 |
| 紧急指针 | 16位,标识紧急数据的末尾位置 |
| 可选字段 | 用于包含一些额外的控制信息,如最大报文段长度、时间戳等。长度可变,最长可达 40 字节 |

备注一般而言,
ACK是 TCP 首部中的一个“开关标志位”(0或1),表示本段是否携带确认;ack是一个"数值字段",表示期望的下一个字节序号。两者需配合使用:ACK=1时ack字段才有效。为了不引起混淆,使用
ACKbit表示ACK,ACKnum表示ack。
连接控制
TCP与UDP协议最大的区别,就是在正式交换数据前需要先建立连接(也称为发送方和接收方的握手)。
三次握手
第1次握手:客户端 $\rightarrow$ 服务端
- 客户端状态:
CLOSED$\rightarrow$SYN-SENT。客户端发送一个带有SYN=1的包,请求建立连接,并建立一个初始序列号seq=x。 - 服务端状态:
LISTEN,等待连接。
第2次握手:服务端 $\rightarrow$ 客户端
- 服务端状态:
LISTEN$\rightarrow$SYN-RCVD。 - 服务端收到
SYN=1的包后,回应一个SYN + ACK的包:SYN=1:表示服务端也同意建立连接ACKbit=1:确认客户端的 SYNseq=y:服务端自己的初始序列号ACKnum=x+1:确认号是客户端序列号 + 1
第3次握手:客户端 $\rightarrow$ 服务端
- 客户端状态:
SYN-SENT$\rightarrow$ESTAB - 客户端收到服务端的
SYN + ACK后,回应一个ACK包:ACKbit=1:确认服务端的SYNACKnum=y+1:确认号是服务端序列号 + 1

使用 TCP 请求服务至少需要 $2\text{RTT}$($1 \text{RTT}$ 建立连接,$1 \text{RTT}$ 请求和接收数据,不考虑连接关闭时间)。
因为实际上 TCP 第三次握手时发送方通常携带了请求内容信息,不需要再发送一次。
四次挥手
第1次挥手:客户端发起关闭请求
- 客户端状态:
ESTAB$\rightarrow$FIN_WAIT_1。 - 客户端动作:应用层调用
close(),发送FINbit=1,seq=x。表示不再发送数据,但仍可以接受数据。
第2次挥手:服务端确认关闭请求
- 服务端状态:
ESTAB$\rightarrow$CLOSE_WAIT。 - 服务端动作:收到
FINbit=1后,发送ACKbit=1,ACKnum=x+1表示确认;同时通知上层应用准备关闭。 - 客户端状态:
FIN_WAIT_1$\rightarrow$FIN_WAIT_2。
第3次挥手:服务端发起关闭请求
- 服务端动作:应用层处理完毕,发送
FIN=1,ACKbit=1,seq=y,ACKnum=x+1。 - 服务端状态:
CLOSE_WAIT$\rightarrow$LAST_ACK。 - 客户端动作: 收到该
FIN报文后,进入TIME_WAIT状态。
第4次挥手:客户端确认服务端关闭
- 客户端动作: 发送
ACKbit=1,seq=x+1,ACKnum=y+1 - 客户端状态:
TIME_WAIT$\rightarrow$ 等待 2 个最大报文生存时间($2\text{MSL}$)后 $\rightarrow$CLOSED - 服务端状态: 收到
ACK后 $\rightarrow$CLOSED

备注第4次挥手后,主动关闭方($A$)要等待 $2 \text{MSL}$ 后才能关闭 TCP 连接:
- 保证最后的
ACK能到达 $B$:如果 $A$ 直接关闭,而最后一个ACK丢失了,B会超时重传FIN。但此时 $A$ 已经关闭连接无法响应,将导致 $B$ 永远无法正常关闭连接(示例见上图)。- 让旧报文“消亡”:防止上一次连接中的“迷路”数据包,在新建立的相同端口的连接中突然出现,造成数据错乱。
滑动窗口
滑动窗口本质上是接收方剩余可用缓存的大小(即还能装下多少数据)。发送方必须根据接收方通告的窗口值来决定自己能发多少“未被确认”的数据。
- 发送窗口(swnd):发送方允许发送但尚未收到确认的数据总量。
- 接收窗口(rwnd):接收方当前空闲的缓存空间,通过ACK报文告知发送方。
- 拥塞窗口(cwnd):发送方内部维护的一个变量,用来限制未确认数据的数量,以避免在网络中引入过多分组而造成拥塞。
窗口结构

在发送方内部,数据被划分为四个区域(以字节编号):
- 已发送且已确认(最左边):可以丢弃,窗口向右滑动。
- 已发送但未确认(窗口内):正在等待
ACK,不能滑动。 - 允许发送但尚未发送(窗口内剩余空间):准备就绪,等待发送。
- 暂时不允许发送(窗口右侧):接收方缓存已满,必须等待窗口滑动后才能发
每当收到一个 ACK,窗口就会向右“滑动”,将新的数据纳入可发送范围。
发送窗口的大小受拥塞窗口与接收窗口的共同影响,数值上等于其中较小的一个:
$$\text{swnd} = \min\{\text{cwnd}, \text{rwnd}\}$$
- $\text{cwnd}$ 由发送方根据
ACK的到达情况动态调整,即拥塞控制。- $\text{rwnd}$ 由接收方的
ACK报文内容提供,发送方据此更新 $\text{rwnd}$。
流量控制
流量控制(TCP Transmission Control Protocol)是一种端到端的机制,旨在协调发送方和接收方之间的数据传输速率,防止接收方因处理能力或缓冲区限制而被压垮,从而避免数据包的丢失和重传。流量控制的核心目标是保证数据的可靠交付,同时尽可能高效地利用可用的网络带宽。
可靠传输
序列号与确认号
序列号与确认号是TCP首部的两个字段。序列号用于记录目前已经发送了哪些数据,确认号记录哪些数据已经被接收方正确接收。
发送方与接收方有各自的序列号计算方法。
确认号数值上等于接收方期待的序号,即本地按照正确接收流程收到的
$$\text{最大数据报文序列号} + 1$$表明该报文之前的信息均已收到。
发送方与接收方的序列号与确认号交互过程:
- 如果发送的数据段丢失了,接收方不会发送更新的确认号。这时发送方将会触发超时并重传丢失的数据段。
- 如果发送的数据段到达了接收方,但是乱序,接收方将续发送最后一个正确序列号的确认号,提示发送方其中有数据段需要重新传输。
- 如果数据段到达了接收方,并且是按顺序的,接收方发送一个新的确认号,提示发送方到目前为止的所有数据都已正确接收。

超时重传
TCP作为一种可靠传输协议,需要确保发送的数据被对方接收并确认。但网络中可能发生各种情况导致数据无法按时到达。
因此,TCP协议在发送一个数据段(segment)时,会为每一个发送的 segment 设置一个重传定时器。如果某个 segment 的定时器超时了,就说明发送方在规定的时间阈值内没有接收到该 segment 的确认号,发送方就会触发超时重传,重新发送超时的 segment。
设置的超时重传时间阈值就记作 $\text{RTO}$。
$\text{RTO}$ 的设置非常讲究,太短会导致过早超时和不必要的重传,太长又会使得响应变慢。
$\text{RTO}$ 的值由下列式子给出:
$$\text{RTO} = \text{EstimatedRTT} + 4 \times \text{DevRTT}$$其中
$$\text{EstimatedRTT} = (1-\alpha) \times \text{EstimatedRTT} + \alpha \times \text{SampleRTT}$$$$\text{DevRTT} = (1-\beta) \times \text{DevRTT} + \beta \times \mid \text{SampleRTT} - \text{EstimatedRTT} \mid$$
$\text{SampleRTT}$ 是每一次报文往返时间的样本,$\text{EstimatedRTT}$ 是加权平均的往返时间,$\text{DevRTT}$ 是往返时间的偏差。通常 $\alpha=0.125$,$\beta=0.25$
拥塞控制
TCP拥塞控制的核心:发送方在数据包丢失(拥塞)前可以一直提高发送速率,直到丢失时才降低发送速率。可以进一步表述为AIMD(加性增加、乘性降低)。
- 加性增加:每经过一个 $\text{RTT}$,将发送的最大段数目增加1。
- 乘性降低:每次发生丢失时将发送速率减半。
慢启动
当一个TCP连接开始后,拥塞窗口cwnd设置为一个很小的值(通常为 $1 \text{MSS}$)。之后,对于每个收到的ACK,cwnd都会增加一个 $\text{MSS}$ 。也就是每个 $\text{RTT}$ 后cwnd都会翻倍,呈现指数增长。
备注$\text{MSS}$ 是 Maxium Segment Size 的简称,指最大报文段长度。
$\text{MSS}$ 通常是根据网络路径的 MTU(最大传输单元)来确定的,MTU 是网络层上一种数据包最大尺寸的限制,常见的 $\text{MSS} = 1460 \text{B}$(MTU 1500 字节减去 IP 头部和 TCP 头部的大小)。
当cwnd达到ssthresh(慢启动阈值)时,TCP 会从慢开始模式转换到拥塞避免模式。

拥塞避免
当TCP进入拥塞避免阶段后,拥塞窗口cwnd的增长方式从慢启动的指数增长转变为线性增长:每收到一个ACK,cwnd增加1/cwnd个 $\text{MSS}$。
由于在一个往返时间($\text{RTT}$)内会收到大约
cwnd个ACK(发送窗口的大小),等价于在每个 $\text{RTT}$ 结束时cwnd只增长约 $1 \text{MSS}$。这种“每 $\text{RTT}$ 加 $1 \text{MSS}$”的线性增长可以避免窗口迅速膨胀,引起网络拥塞。
TCP检测到网络拥塞后,将慢启动阈值(ssthresh)设置为当前cwnd的一半($\displaystyle \text{ssthresh} = \frac{\text{cwnd}}{2}$),然后按照以下情况分别处理:
- 收到3个冗余的
ACK:触发快速重传,设置 $\displaystyle \text{cwnd} = \frac{\text{cwnd}}{2} + 3$ - 出现超时:
cwnd重新设为 $1 \text{MSS}$,相当于回到最保守的状态。
快速恢复
快速重传:当发送方连续收到三个冗余的
ACK时,说明网络中已经有一个数据段丢失,而后续的ACK只能确认已经成功到达的后续段。此时,TCP就会立即重传那个被认定为丢失的段,而不必等到重传计时器超时,这一过程被称为快速重传。
触发快速重传后,TCP会切换到快速恢复(Fast Recovery)阶段:
- 每收到一个重复
ACK,$\text{cwnd} = \text{cwnd} + \text{MSS}$。
这种线性增长相当于用已经确认的
ACK为窗口“买票”,使得发送方能够继续发送新数据,而不会因为单个丢包而立刻停滞。
- 收到一个新的、非重复的
ACK后,表明丢失的数据段已经被成功重传并得到确认,TCP退出快速恢复阶段。此时,将cwnd设置为ssthresh(即把窗口恢复到新的慢启动阈值)随后进入拥塞避免阶段。


附录
可靠数据传输原理
计算机网络中,底层数据的传输信道往往是不可靠的,可能会出现丢包和乱序。可靠数据传输协议(rdt)的目标就是在不可靠的信道上,为上层应用提供一个可靠的服务,让接收方能完整、按顺序、无差错地收到发送方的数据。

rdt协议
rdt1.0:完全可靠的信道
- 假设信道不会出错、不会丢包。
- 发送方只管发,接收方只管收。
- 没有 ACK、没有序列号、没有重传(过于理想,现实中不存在)

rdt2.0:只存在比特位的差错(无丢包)
- 引入了 ACK/NAK,标识数据包的确认/否认。
- 接收方收到正确地数据包则恢复ACK,反之回复NAK;发送方收到NAK则重传上一个数据包。

rdt2.0 的问题:
如果 ACK/NAK 本身在传输中损坏了怎么办?发送方不知道接收方是否收到。
rdt2.1:解决 ACK/NAK 损坏问题
- 为每个数据包加上序列号(0或1)。
- 发送方交替发送序列号为0和1的数据包,接收方通过序列号判断是新包还是旧包的重传。

rdt2.2:去掉了 NAK,只用 ACK
- 每个 ACK 要指明它确认的是哪个序列号。
- 如果接收方收到错包,就重复上一次正确的 ACK,相当于隐式 NAK。
- 冗余ACK表明发送端的当前数据出错,需要重发。

rdt3.0:着重解决比特差错和丢包、它在 rdt2.2 的基础上增加超时重传。发送方发送一个包后启动一个定时器:
- 如果在超时时间内收到 ACK:正常发送下一个包。
- 如果超时未收到 ACK:重传当前包(可能因为包丢了,也可能是ACK丢了)。

在 rdt 协议中,信道利用率(也叫做链路利用率)是指信道用于传输有效数据的效率,通常定义为成功传输数据的时间占总传输时间的比例。它反映了协议在给定信道条件下的性能,是评估 ARQ 协议效率的重要指标。即
$$U =\frac{T_{\text{data}}}{T_{\text{total}}}$$停-等协议与流水线
rdt3.0 是停-等协议:发送一个包 → 等待ACK → 发下一个。信道利用率很低,协议设计不好会限制信道的性能。

信道利用率为
$$U = \frac{T_\text{data}}{\text{RTT} + T_{\text{data}} + T_{\text{ack}}}$$TCP使用流水线机制:允许发送方发送多个“正在进行中”但尚未被确认的数据包。

信道利用率为
$$U = \frac{N \cdot T_\text{data}}{\text{RTT} + T_{\text{data}} + T_{\text{ack}}}$$这也意味着必须增加序列号的范围,同时在发送方/接收方之间建立缓冲。
Go-Back-N
Go-Back-N(GBN)是计算机网络中一种实现可靠数据传输的流水线协议,用于解决 rdt3.0 信道利用率低的问题。
Go-Back-N 的核心思想:
- 发送方可以在未收到确认的情况下,连续发送多个数据包,但接收方只允许按顺序接收;
- 一旦某个包丢失或出错,接收方会丢弃所有后续包,发送方则回退(Go-Back)到那个丢失的包,重新发送该包及其之后的所有包($N$个包)。

Go-Back-N 的一个主要缺点:当有一个包丢失或损坏时,GBN 会重传从丢失包开始之后的所有包(即使很多包已经被接收方正确收到),从而浪费大量带宽。
选择重传
选择重传(Selective Repeat,SR)是另一种流水线可靠数据传输协议,它解决了 Go-Back-N 的上述主要缺点。
SR 的核心思想:只重传真正丢失或出错的个别包,而接收方会缓存所有已正确接收但顺序不连续的包,等待缺失的包到达后再一并排序上交。

拥塞控制方法
| 控制方法 | 缺点 |
|---|---|
| 无丢包(无限缓冲区) | 拥塞仍会导致延迟灾难,说明 “仅靠增大缓冲区无法解决拥塞” |
| 有限缓冲区+重传 | 暴露 “丢包 - 重传 - 负载增加” 的恶性循环,说明 “重传无法避免拥塞,反而可能加剧问题” |
| 多跳路径 | 末端链路拥塞会导致上游资源全被浪费,说明 “拥塞的代价会沿路径扩散,影响全局效率” |
拥塞控制的核心在于控制发送速率,让发送端速率与网络能力动态匹配,减少无效流量。