TCP可靠传输及流量控制系列三:重传时间RTO计算

时间:2022-12-30 22:36:14

RTO的计算

1、数据结构

TCP中用于计算重传时间RTO的数据结构定义在tcp协议控制块中。

struct tcpcb {

.......

/*动态计算的数据包发送确认的往返时间*/

short t_rtt; /* round trip time */

/*标记设定了重传定时器的报文的开始序号*/

tcp_seq t_rtseq; /* sequence number being timed */

/*已平滑的rtt*/

short t_srtt; /* smoothed round-trip time */

/*已平滑的RTT的平均偏差估计器*/

short t_rttvar; /* variance in round-trip time */

/*重传时限最小值*/

u_short t_rttmin; /* minimum rtt allowed */

short t_rxtshift; /* log(2) of rexmt exp. backoff */

/*当前的重传时间*/

short t_rxtcur; /* current retransmit value */

/*指数退避的数组索引*/

u_long max_sndwnd; /* largest window peer has offered */

......

}

2、计算公式

T C P的一个基本操作是在发送了需对端确认的报文段后,设置重传定时器。如果在定时器时限范围内未收到A C K,该报文段被重发。T C P要求对端确认所有数据报文段,不携带数据的报文段则无需确认(例如纯A C K报文段)。如果估算的重传时间过小,响应到达前即超时,造成不必要的重传;如果过大,在报文段丢失之后,发送重传报文段之前将等待一段额外的时间,降低了系统的效率。更为复杂的是,主机间的往返时间动态改变,且变化范围显著。

T C P计算重传时限( RTO )时不仅要测量数据报文段的往返时间RTT(nticks),还要记录已平滑的RT T估计器(srtt)和已平滑的RT T平均偏差估计器(rttvar)。平均偏差是标准方差的良好近似,计算较为容易,无需标准方差的求平方根运算。

d e l t a = n t i c k s-s rt t

s rt t←s rt t + g×d e l t a

rt t v a r←rt t v a r + h(| d e l t a |-rt t v a r)

RTO = s rt t +4×rt t v a r

d e l t a是最新测量的往返时间(n t i c k s)与当前已平滑的RT T估计器(s rt t)间的差值;

g是用到RT T估计器的增益,设为1 / 8;

h是用到平均偏差估计器的增益,设为1 / 4。

这两个增益和RTO计算中的乘数4有意取为2的乘方,从而无需乘、除法,只需简单的移位操作就能够完成运算。

3、源码分析

void

tcp_xmit_timer(tp, rtt)

register struct tcpcb *tp;

short rtt;

{

register short delta;

tcpstat.tcps_rttupdated++;

if (tp->t_srtt != 0) {

/*

 * srtt is stored as fixed point with 3 bits after the

 * binary point (i.e., scaled by 8).  The following magic

 * is equivalent to the smoothing algorithm in rfc793 with

 * an alpha of .875 (srtt = rtt/8 + srtt*7/8 in fixed

 * point).  Adjust rtt to origin 0.

 */

更新已平滑的RT T估计器

delta = rtt - 1 - (tp->t_srtt >> TCP_RTT_SHIFT);

tcpnewtcpcb初始化已平滑的RTT估计器(tsrtt)为0,指明连接上不存在RTT估计器。delta是RTT测量值与当前已平滑的RTT估计器间的差值,以未缩放的滴答为单位。t s r t t除以8,单位从缩放后的滴答转换为未缩放的滴答。

if ((tp->t_srtt += delta) <= 0)

tp->t_srtt = 1;

已平滑的RT T估计器用以下公式进行更新:

s rt t←s rt t+ g×d e l t a

由于增益g = 1 / 8,公式变为

8×s rt t←8×s rt t+ d e l t a

也就是

t s r t t←t s r t t+d e l t a

/*

 * We accumulate a smoothed rtt variance (actually, a

 * smoothed mean difference), then set the retransmit

 * timer to smoothed rtt + 4 times the smoothed variance.

 * rttvar is stored as fixed point with 2 bits after the

 * binary point (scaled by 4).  The following is

 * equivalent to rfc793 smoothing with an alpha of .75

 * (rttvar = rttvar*3/4 + |delta| / 4).  This replaces

 * rfc793's wired-in beta.

 */

已平滑的RT T平均偏差估计器

if (delta < 0)

delta = -delta;

delta -= (tp->t_rttvar >> TCP_RTTVAR_SHIFT);

if ((tp->t_rttvar += delta) <= 0)

tp->t_rttvar = 1;

} else {

第一次估计SRTT

/* 

如果是首次测量某连接的RT T值,已平滑的RT T估计器初始化为测量得到的样

本值。

 * No rtt measurement yet - use the unsmoothed rtt.

 * Set the variance to half the rtt (so our first

 * retransmit happens at 3*rtt).

 */

tp->t_srtt = rtt << TCP_RTT_SHIFT;

平均偏差等于测量到的RT T值的一半

tp->t_rttvar = rtt << (TCP_RTTVAR_SHIFT - 1);

}

下一个报文的发送和计时做准备

tp->t_rtt = 0;

tp->t_rxtshift = 0;

RT T计数器(trtt)和重传移位计数器(trxtshift)同时复位为0,为下一个报文的发送和计时做准备

/*

 * the retransmit should happen at rtt + 4 * rttvar.

 * Because of the way we do the smoothing, srtt and rttvar

 * will each average +1/2 tick of bias.  When we compute

 * the retransmit timer, we want 1/2 tick of rounding and

 * 1 extra tick because of +-1/2 tick uncertainty in the

 * firing of the timer.  The bias will give us exactly the

 * 1.5 tick we need.  But, because the bias is

 * statistical, we have to test that we don't drop below

 * the minimum feasible timer (which is 2 ticks).

 */

连接的下一个RTO(t_rxtcur)计算

TCPT_RANGESET(tp->t_rxtcur, TCP_REXMTVAL(tp),

    tp->t_rttmin, TCPTV_REXMTMAX);

#define TCP_REXMTVAL(tp) \

(((tp)->t_srtt >> TCP_RTT_SHIFT) + (tp)->t_ttvar)

其实,这就是我们很熟悉的公式

RTO=s rt t+ 4×rt t v a r

/*

 * We received an ack for a packet that wasn't retransmitted;

 * it is probably safe to discard any error indications we've

 * received recently.  This isn't quite right, but close enough

 * for now (a route might have failed after we sent a segment,

 * and the return path might not be symmetrical).

 */

tp->t_softerror = 0;

}