TCP协议理解
1) RTT 的计算
1.1 (加权值, ,针对重传出现的两个ack的确认二义性(acknowledgement ambiguity),采用karn算法,即忽略重传包的计算,但是使用定时器补偿 timer backoff, 对每次重传进行时限补偿,知道成功发送一个包,停止计算。
new_timeout=r * timeout ,r=2
然后对于没有重传的包,再重新计算RTT, 并设定timeout
RTT=(a*old_rtt)+(1-a)*new_rtt
Timeout=b*RTT
karn算法在分组丢失率高的网络上也能很好的工作
1.2 对于时延变化较大的情况
rtt 假药估计往返时间均值,也要估算方差,
2)拥塞的控制
两个窗口,对端的通知窗口(receiver_advertisement),和自己计算的拥塞窗口 (congestion_window)
allowed_window = min (receiver_advertisement, congestion_window)
思想是认为丢包是路由器拥塞造成
避免拥塞的方法慢启动(slow-start)和加速递减(multiplicative decrease)
一旦发现丢包就把拥塞窗口(congestion_window)减半,收到ack后,在没收到一个byte确认,congestion窗口就加1
3) 对于糊涂窗口综合症(siliy window syndrome)
发送快于接收,接收满后,如果应用程序读走一个字节,这时窗口,发送方只能一个字节一个字节发
接收方只有等到缓冲区至少达到1半总空间或者最大报文长度
发送方采用nagle算法
如果有unacked packet以及发送数据很小时,等到数据长度足够或者unacked packet被确认,再发
(问题是,如果线路延迟大,等到ack后,再发岂不是效率很低)
|
|
经典TCP拥塞算法介绍 |
日期 |
2021/1/15 17:42:00 |
版本号 |
v1.0 |
目录
1. 拥塞算法目的... 1
2. TCP拥塞算法类型... 1
3. TCP拥塞算法原理... 1
3.1. Reno原理... 1
3.2. Vegas原理... 2
3.3. BBR原理... 2
3.4. Remy原理... 3
1. 拥塞算法目的
通过有效的策略控制拥塞窗口的大小。
拥塞窗口代表中间网络的处理能力。
2. TCP拥塞算法类型
控制方式 |
举例算法 |
基于丢包的拥塞控制 |
Reno、New Reno、SACK、Cubic |
基于时延的拥塞控制 |
Vegas、FastTCP |
基于链路容量的拥塞控制 |
BBR |
基于学习的拥塞控制 |
Remy |
3. TCP拥塞算法原理
3.1. Reno原理
Reno算法 |
|
适用场景 |
低延时、低带宽的网络 |
基本原理 |
慢启动、拥塞避免、快重传和快恢复 慢启动:每收到一个 ACK 就将拥塞窗口大小加1MSS,每轮次发送窗口增加1倍,当窗口达到慢启动阈值时,进入拥塞避免阶段,窗口每轮次加1MSS,当窗口未达到慢启动阀值就出现丢包则(快重传?)慢启动阀值减半,重新开始慢启动。 拥塞避免:窗口大小每轮次加1MSS,报文丢失时进入快重传阶段。 快重传和快恢复:把ssthresh设置为cwnd的一半, 把cwnd再设置为ssthresh的值(具体实现有些为ssthresh+3),然后重传丢失的报文段,加3的原因是因为收到3个重复的ACK,表明有3个“老”的数据包离开了网络。再收到重复的ACK时,拥塞窗口增加1。当收到新的数据包的ACK时,把cwnd设置为第一步中的ssthresh的值。原因是因为该ACK确认了新的数据,说明从重复ACK时的数据都已收到,该恢复过程已经结束,可以回到恢复之前的状态了,也即再次进入拥塞避免状态。 NewReno在快恢复阶段需要收到该窗口内所有数据包的确认后才会退出快速恢复状态。 |
优点 |
仅适合低带宽低时延网络 |
缺点 |
高带宽时延网络中,宽带利用率低 |
主要接口参数 |
慢启动阈值、duplicate acks==3(丢包) |
伪代码实现: 慢启动: u32 tcp_slow_start(struct tcp_sock *tp, u32 acked) { u32 cwnd = min(tp->snd_cwnd + acked, tp->snd_ssthresh); acked -= cwnd - tp->snd_cwnd; tp->snd_cwnd = min(cwnd, tp->snd_cwnd_clamp); return acked; } 拥塞避免: void tcp_cong_avoid_ai(struct tcp_sock *tp, u32 w, u32 acked) { /* If credits accumulated at a higher w, apply them gently now. */ if (tp->snd_cwnd_cnt >= w) { tp->snd_cwnd_cnt = 0; tp->snd_cwnd++; } tp->snd_cwnd_cnt += acked; if (tp->snd_cwnd_cnt >= w) { u32 delta = tp->snd_cwnd_cnt / w; tp->snd_cwnd_cnt -= delta * w; tp->snd_cwnd += delta; } tp->snd_cwnd = min(tp->snd_cwnd, tp->snd_cwnd_clamp); } |
3.2. Cubic原理
Cubic算法 |
|
适用场景 |
高带宽、低丢包率网络,能够有效利用带宽 |
基本原理 |
C 是调节因子,t 是从上一次缩小拥塞窗口经过的时间,Wmax 是上一次发生拥塞时的窗口大小, β是乘法减小因子。 |
优点 |
只要没有出现丢包,就不会主动降低自己的发送速度,可以最大程度的利用网络剩余带宽,提高吞吐量,在高带宽、低丢包率的网络中可以发挥较好的性能。 |
缺点 |
Bufferbloat(缓冲区膨胀),缓冲区越大,时延就越高,丢包率上升,网络抖动大。 |
主要接口参数 |
C、β、t、Wmax |
伪代码实现: |
3.3. Vegas原理
Vegas算法 |
|
适用场景 |
网络中只存在 Vegas 一种拥塞控制算法,竞争公平的情况 |
基本原理 |
Vegas将时延 RTT 的增加作为网络出现拥塞的信号,RTT 增加,拥塞窗口 减小,RTT 减小,拥塞窗口增加。具体来说,Vegas 通过比较实际吞吐量和期望吞吐量 来调节拥塞窗口的大小, 期望吞吐量:Expected = cwnd / BaseRTT, 实际吞吐量:Actual = cwnd / RTT, diff = (Expected-Actual) * BaseRTT, BaseRTT 是所有观测来回响应时间的最小值,一般是建立连接后所发的第一个数据包的 RTT,cwnd 是目前的拥塞窗口的大小。Vegas 定义了两个阈值 a,b,当 diff > b 时,拥塞窗口减小,当 a <= diff <=b 时,拥塞窗口不变,当 diff < a 时,拥塞窗口增加。 |
优点 |
Vegas 算法采用 RTT 的改变来判断网络的可用带宽,能精确地测量网络的可用带宽,效率比较好。 |
缺点 |
Vegas 与基于丢包的拥塞控制算法共存的情况下,当网络缓冲区被填满时,将导致 Vegas 计算的 RTT 增大,进而降低拥塞窗口,使得传输速度越来越慢,因此 Vegas 未能在 Internet 上普遍采用。 |
主要接口参数 |
BaseRTT、RTT、Min阀值、Max阀值 |
伪代码实现:
|
3.4. BBR原理
BBR算法 |
|
适用场景 |
高带宽、高时延、有一定丢包率的长肥网络,可以有效降低传输时延,并保证较高的吞吐量。 |
基本原理 |
周期性地探测网络的容量,交替测量一段时间内的带宽极大值和时延极小值,将其乘积作为作为拥塞窗口大小,使得拥塞窗口始的值始终与网络的容量保持一致。BBR中保留了慢启动部分。 |
优点 |
高延时、高丢包率环境下性能下降小。 由于不会填满网络缓冲区,时延一直处于较低状态。 |
缺点 |
Vegas 与基于丢包的拥塞控制算法共存的情况下,当队列缓存较大时,RTT变大导致BBR带宽减小。 |
接口参数 |
历史时间窗口中的带宽极大值、时延极小值。 struct rate_sample { u64 prior_mstamp; /* starting timestamp for interval */ u32 prior_delivered; /* tp->delivered at "prior_mstamp" */ s32 delivered; /* number of packets delivered over interval */ long interval_us; /* time for tp->delivered to incr "delivered" */ u32 snd_interval_us; /* snd interval for delivered packets */ u32 rcv_interval_us; /* rcv interval for delivered packets */ long rtt_us; /* RTT of last (S)ACKed packet (or -1) */ int losses; /* number of packets marked lost upon ACK */ u32 acked_sacked; /* number of packets newly (S)ACKed upon ACK */ u32 prior_in_flight; /* in flight before this ACK */ bool is_app_limited; /* is sample from packet with bubble in pipe? */ bool is_retrans; /* is sample from retransmission? */ bool is_ack_delayed; /* is this (likely) a delayed ACK? */ }; |
pacing_gain决定实际发包速率 cwnd_gain决定窗口 STARTUP: pacing_gain 2.89,cwnd_gain 2.89 DRAIN: pacing_gain 0.35, cwnd_gain 2.89 PROPEBW:pacing_gain [1.25,0.75,1,1,1,1,1,1], cwnd_gain 2 PROPERTT: pacing_gain 1 cwnd_gain 1(cwnd固定大小4) 伪代码实现: bbr_update_bw 1判断RTT采样是否结束 2长期带宽采样 采样规则4 < lt_rtt_cnt < 16 && losses 当采样带宽相近时,bbr->lt_bw = (bw + bbr->lt_bw) >> 1 3 计算瞬时带宽并保存最大值,当RTT采样时不记录最大带宽。 bbr_update_cycle_phase BBR_PROBE_BW模式下,在rtt变大时周期性调整发送大小 bbr_check_full_bw_reached 检查带宽连续三次增长是否超过历史最大值的1.25倍 bbr_check_drain 由BBR_STARTUP转入BBR_DRAIN模式 在BBR_DRAIN模式下,如果inflight <=BDP则进入BBR_PROBE_BW模式 bbr_update_min_rtt 当min_rtt超过10s没有更新,进入BBR_PROBE_RTT模式, 设置拥塞窗口最大为4 当检测到飞行中的包小于等于4时,开始采样,该模式维持在200ms以上 在整个过程中一旦出现TCP_CA_Loss,则full bw清零重新开始探测 若出现丢包则cwnd=inflight+acked |
3.5. Remy原理
BBR算法 |
|
适用场景 |
网络环境为复杂的异构网络,希望计算机能够针对不同网络场景自动选择合适的拥塞控制方式,要求现有的网络模型能够覆盖所有可能出现情况。 |
基本原理 |
采用机器学习的方式生成拥塞控制算法模型。通过输入各种参数模型(如瓶颈链路速率、时延、瓶颈链路上的发送者数量等),使用一个目标函数定量判断算法的优劣程度,在生成算法的过程中,针对不同的网络状态采用不同的方式调整拥塞窗口,反复修改调节方式,直到目标函数最优,最终会生成一个网络状态到调节方式的映射表,在真实的网络中,根据特定的网络环境从映射表直接选取拥塞窗口的调节方式。 |
优点 |
自适应范围广,适合复杂网络 |
缺点 |
比较依赖输入的训练集 |
主要接口参数 |
瓶颈链路速率、时延、瓶颈链路上的发送者数量等 |
4. Linux内核TCP拥塞算法通用框架
struct tcp_congestion_ops { struct list_head list; u32 key; u32 flags;
/* initialize private data (optional) */ void (*init)(struct sock *sk); /* cleanup private data (optional) */ void (*release)(struct sock *sk);
/* return slow start threshold (required) */ u32 (*ssthresh)(struct sock *sk); /* do new cwnd calculation (required) */ void (*cong_avoid)(struct sock *sk, u32 ack, u32 acked); /* call before changing ca_state (optional) */ void (*set_state)(struct sock *sk, u8 new_state); /* call when cwnd event occurs (optional) */ void (*cwnd_event)(struct sock *sk, enum tcp_ca_event ev); /* call when ack arrives (optional) */ void (*in_ack_event)(struct sock *sk, u32 flags); /* new value of cwnd after loss (required) */ u32 (*undo_cwnd)(struct sock *sk); /* hook for packet ack accounting (optional) */ void (*pkts_acked)(struct sock *sk, const struct ack_sample *sample); /* override sysctl_tcp_min_tso_segs */ u32 (*min_tso_segs)(struct sock *sk); /* returns the multiplier used in tcp_sndbuf_expand (optional) */ u32 (*sndbuf_expand)(struct sock *sk); /* call when packets are delivered to update cwnd and pacing rate, * after all the ca_state processing. (optional) */ void (*cong_control)(struct sock *sk, const struct rate_sample *rs); /* get info for inet_diag (optional) */ size_t (*get_info)(struct sock *sk, u32 ext, int *attr, union tcp_cc_info *info);
char name[TCP_CA_NAME_MAX]; struct module *owner; }; |
int tcp_register_congestion_control(struct tcp_congestion_ops *type); void tcp_unregister_congestion_control(struct tcp_congestion_ops *type); |
本文来自博客园,作者:{e_shannon},转载请注明原文链接:https://www.cnblogs.com/e-shannon/p/14307981.html