TCP Friendly Rate Control(TFRC),是网络环境下单播流的一种拥塞控制机制。对于TCP流,它是公平竞争带宽的。但是与TCP相比,吞吐量随时间的变化要小得多,也就是对带宽变化的响应比TCP慢,使其更适用于电话通信、流媒体等需要相对平滑发送速率的应用。因此,TFRC仅用于需要平滑吞吐量时,尤其是避免TCP响应单个丢包而将发送速率减半。推荐使用TCP发送尽可能多的数据包,或者不需要可靠机制,可以使用加法增加、乘法减小(AIMD)的拥塞控制方案,与TCP使用的参数类似。TFRC是为发送固定大小的数据包的应用程序而设计,以改变每秒发送包数来响应拥塞。但是,一些音频应用需要固定的发送时间间隔,改变发送数据包大小代替改变发送数据包数量,来响应网络拥塞。TFRC是基于接收方的机制,通过数据接收方来计算拥塞控制信息而不是发送方。
目录
1. 协议机制
1.1 吞吐量方程
1.2 传输包内容
2. 数据发送协议
2.1 发送端初始化
2.2 发送端收到反馈包的响应
2.3 无反馈定时器超时
2.4 数据包传输的调度
3. 丢包率p的计算
3.1 丢失/标记数据包的检测
3.2 从丢失历史到丢失事件的转换
3.3 内部丢失事件间隔
3.4 平均丢失间隔
4. 数据接收端协议
4.1 接收端收到数据包的响应
4.2 反馈定时器超时
4.3 接收端初始化
5. 计算问题
6. 安全问题
在介绍协议之前,先探讨几个相关问题,以便我们带着问题寻找答案。
1)如何计算TCP发送速率,由哪些参数构成;
2)什么是往返时间,如何计算;
3)根据什么信息来改变发送速率,如何改变发送速率;
4)数据包的序列号如何变化的,重传包序列号与丢失包序列号是否相同;
5)如何判定为丢包事件;
6)拥塞控制机制是否安全,会不会受到攻击;
对于拥塞控制机制,TFRC直接使用允许发送速率的吞吐量方程作为丢包率和往返时间(RTT)的函数,为了TCP的公平竞争,TFRC使用吞吐量方程,它把TCP发送速率粗略地描述为丢包率、往返时间和数据包大小的函数。我们将丢失事件定义为来自数据窗口的一个或多个丢失/标记的数据包,其中标记数据包是指显示拥塞通知(ECN)的拥塞指示。拥塞控制机制工作原理如下:
① 接收方测量丢包率并反馈给发送方;
② 发送方根据反馈信息计算往返时间RTT;
③ 丢包率和RTT输入到吞吐量方程,给出合理的传输速率;
④ 发送方调整发送速率来匹配目标发送速率;
TCP吞吐量方程必须反映超时重传的行为,因为它在更高丢包率下控制TCP吞吐量。接下来介绍的是TCP的Reno精简版的吞吐量方程。我们更倾向于选择基于SACK的TCP吞吐量方程,虽然目前为止没有人推导出基于SACK的吞吐量方程,但是综合仿真与实验,两者之间差异很小。吞吐量方程如下:
s
X = --------------------------------------------------------------
R*sqrt(2*b*p/3) + (t_RTO * (3*sqrt(3*b*p/8) * p * (1+32*p^2)))
其中X为传输速率,单位是bytes/second;
s为数据包大小,单位是byte;
R为往返时间RTT,单位是second;
p为丢包率,范围在0~1之间;
t_RTO为重传定时器的时间,单位是second;
b为单个TCP所确认的数据包数量;
可以把t_RTO精简为t_RTO=4*R,或者t_RTO=max(4R, 1s)。TCP连接一般使用延迟确认,发送两个数据包才应答一个确认包,所以发送速率b=2。如果需要立即应答,那么发送速率b=1。
数据包:每个发送的数据包包括如下信息。
① sequence number序列号,每个传输数据包的序列号会递增;
② timestamp时间戳,标识数据包的发送时间;
③ estimate RTT,发送端估计的往返时间;
反馈包:每个接收端发送的反馈包包括如下信息。
① 接收上一个数据包的时间戳,使用t_recvdata表示;
② 接收端接收到上一个数据包所经历的时间;
③ 接收端根据上次发送的反馈报告,估算接收速率;
数据发送端以受控速率向接收端发送数据包。当接收到来自接收端的反馈包时,发送端会根据反馈报告信息来改变发送速率。如果发送端在两个往返时间RTT内没有接收到反馈报告,就会把发送速率减半。发送端协议经历如下步骤:
① 计算即将发送数据包的大小;
② 发送端接收到反馈包的响应;
③ 发送端在无反馈定时器超时的响应;
④ 防止波动;
⑤ 非实时操作系统的传输调度;
为了初始化发送端,参数X设为1 packet/second,无反馈定时器在2s后设为超时。R和t_RTO的初始值为未定义,等待收到反馈包才赋值。
发送端根据自己当前发送速率X,来估算当前往返时间R,并且估算超时间隔t_RTO。在发送端在时间t_now收到反馈包,会执行如下计算步骤:
① 计算新的往返时间RTT:R_sample = (t_now - t_recvdata) - t_delay
② 更新往返时间估算值
之前没有收到反馈包:R = R_sample
之前已收到过反馈包:R = q*R + (1-q)*R_sample(TFRC对滤波常数q不敏感,但推荐默认值为0.9)
③ 更新超时间隔:t_RTO = 4*R
④ 更新发送速率
if p>0:X = max(min(X_calc, 2*X_recv), s/t_mbi)
else: X = max(min(2*X, 2*X_recv), s/R)
注意:如果p=0,说明处于慢启动模式,每经历一个RTT会以两倍速去增大发送速率,直到发生丢包情况。
⑤ 在超过max(4*R, 2*s/X)时间后,重置无反馈定时器超时时间。
如果无反馈定时器超时,发送端需要执行如下步骤:
① 将发送速率减半,如果发送端收到来自接收端的反馈,修改的是发送端缓存X_recv(接收速率)的副本。因为发送速率最大值限制为接收速率X_recv的两倍,修改X_recv会限制当前发送速率,但是在没有丢包的情况下,允许发送端处于慢启动模式时经历每个RTT将发送速率两倍增长。
If (X_calc > 2*X_recv)
X_recv = max(X_recv/2, s/(2*t_mbi));
Else
X_recv = X_calc/4;
② 如果当发送端还没有任何RTT样本,并且没有收到来自接收端的反馈,无反馈定时器超时了,步骤1可以跳过,将发送速率减半:
X = max(X/2, s/t_mbi)
③ 在超过max(4*R, 2*s/X)秒后,重置无反馈定时器的超时时间。
由于TFRC是基于速率的,而且操作系统无法精确调度事件,所以在发送数据包时需要根据时机调节,这样即使操作系统的进程粒度或不规则调度也能保证正确的平均速率。因此,发送时需要计算发送间隔:t_ipi = s/X_inst。当发送端首次发送数据时间为t_0,计算t_ipi,并为第一个包计算时间t1=t_0+t_ipi。接着第二个包时间t2=t1+t_ipi,这样依此类推累加。参数delta用于允许发送数据包的时间存在一定灵活度。如果操作系统有调度时间粒度t_gran,那么delta计算公式为:delta = min(t_ipi/2, t_gran/2)。大多数Unix系统的时间粒度t_gran默认为10ms。假如t_gran的数值不确定,建议初始化为10ms。
对于TFRC来说,获取精确的、稳定测量的丢包率是至关重要的。丢包率是在接收端进行测量,基于对到达数据包的序列号进行丢失或标记检测。
TFRC假定所有数据包都包含一个递增的序列号,因此如果发生丢包需要重传,并且重传会给定一个新的序列号,而不是与原来丢失包的序列号相同。如果传输协议要求重传序列号与原来丢包序列号一致,协议设计者需要弄清楚如何区分重传包的延迟与如何检测丢包重传。接收端需要维持一个数据结构来追踪哪些包到达、哪些包丢失。丢失包的判定,需要至少3个到达包序列号大于该丢失包序列号。
TFRC要求丢包率对几个连续丢包具有鲁棒性,其中这些丢包来自同一个丢失事件。这与TCP类似,在任何一个RTT期间的拥塞窗口只执行一次速率减半。所以接收端需要把丢失历史映射到丢失事件记录中,当在一个RTT期间丢失事件是指丢失一个或多个数据包。为了执行此操作映射,接收端需要使用到发送端定期提供的作为控制信息携带于数据包中的RTT。TFRC对于RTT精度不敏感,推荐是发送端进行计算测量。为了区分丢失或者标志的数据包属于新的丢失事件还是已经存在的丢失事件,我们需要比较已到达数据包的序列号和时间戳。使用到的变量声明如下:
S_loss:丢包的序列号;
S_befor:丢包上一个数据包的序列号;
S_after:丢包下一个数据包的序列号;
T_before:S_before的到达时间;
T_after:S_after的到达时间;
针对某个丢包,我们可以在接收端到达时间S_before与S_after之间内插一个"到达时间",计算公式如下:
T_loss = T_before + ( (T_after - T_before)
* (S_loss - S_before)/(S_after - S_before));
假定一个丢失间隔,由当前数据包序列号S_A与下一个丢失间隔的数据包序列号S_B决定,那么数据包丢失间隔A=S_B - S_A。
为了计算丢包率p,首先需要计算平均丢失间隔。这是通过滤器实现的,该过滤器对最近n个丢失事件间隔进行加权,使得计算出来的丢包率平滑变化。从w_0到w_(n-1)的权重计算如下:
If (i < n/2)
w_i = 1;
Else
w_i = 1 - (i - (n/2 - 1))/(n/2 + 1);
假设n=8,那么从w_0到w_7的权重为:1.0, 1.0, 1.0, 1.0, 0.8, 0.6, 0.4, 0.2
接收端周期性地发送反馈信息给发送端。通常地,反馈包至少在一个RTT周期内发送一次,除非发送端的发送速率小于单个RTT数值,也就是每接收到一个数据包就发送一个反馈包。无论是发生一个新的丢包事件没有等到RTT结束,还是接收到的无序数据包并从历史记录中移除丢失事件,都需要发送反馈包。如果发送端以高速率(一个RTT发送多个数据包)传输,每个RTT发送多次周期性反馈信息有些优点:对变化的RTT测量做出更快响应,对反馈数据包丢失有更强的恢复能力。
检测判断接收到的数据包会不会导致历史记录被填满,因此两个丢失间隔需要合二为一。如果是这种情况,接收端需要立即发送反馈。
当接收端的反馈定时器超时,是否采取措施取决于从上一次发送反馈开始,有没收到新的数据包。假定接收端目前数据包最大序列号为S_m,对应序列号S_m的数据包的RTT为R_m。从上次发送的反馈开始,如果有收到数据包,接收端需要执行如下步骤:
① 计算平均丢包率;
② 计算接收速率X_recv,基于上一个R_m周期内接收到的数据包;
③ 发送反馈包;
④ 在R_m秒后,重置反馈定时器的超时时间;
接收端参数是在收到第一个数据包后进行初始化的,假定当前数据包的序列号为i。当收到第一个数据包后执行如下操作:
① 设置p=0;
② 设置X_recv=0;
③ 准备发送一个反馈包;
④ 在R_i秒后,设置反馈定时器超时;
假定t_RTO=4*R,b=1,那么吞吐量方程表达式如下:
s
X = --------
R * f(p)
其中,f(p) = sqrt(2*p/3) + (12*sqrt(3*p/8) * p * (1+32*p^2))
TFRC本身不是传输协议,而是与传输协议结合使用的拥塞控制机制。因此,需要考虑特定传输协议的安全性与身份认证机制。拥塞控制机制可能被用来创建拒绝服务,这可能是通过欺骗反馈实现的。所以任何使用TFRC的传输协议,注意确保只接收来自接收端的反馈。另外,拥塞控制机制可能被贪婪的接收端操纵,期望占用超出其公平分配的网络带宽。接收端可以通过声称接收到事实上由于拥塞而丢失的数据包,来获取超额网络带宽。这种临时消息细节将取决于传输协议是否可靠。
相关问题的解答:
1)TCP发送速率由丢包率、往返时间和数据包大小构成的函数来计算;
2)往返时间是指从发送开始计时t1,接收端收到数据包反馈给发送端,然后发送端收到反馈包的时间t2,那么往返时间RTT=t2-t1;
3)根据接收端反馈信息来改变发送速率。当处于慢启动模式时,每经历一个RTT会以两倍速增大发送速率,直到发生丢包。当发送端超过2个RTT没收到反馈包,将发送速率减半;
4)数据包的序列号是保持递增的,每发送一个数据包序列号+1,重传包序列号与丢失包序列号不相同;
5)3个到达数据包的序列号大于丢失包序列号判定为丢包事件;
6)拥塞控制机制的安全性取决于传输协议,拥塞控制机制可能通过欺骗反馈被用来创建拒绝服务。另外,可能被贪婪的接收端操纵以占用超预期的网络带宽;
参考协议:rfc3448
可以到GitHub一起学习音视频:https://github.com/xufuji456/FFmpegAndroid