滑动窗口数据结构和发送算法

 

3.4. 发送端算法
3.4.1.    数据结构和变量
A. SND PKT历史窗口:一个循环数组记录每个数据包的开始时间
B. 发送端丢失链表:发送段丢失列表是一个连接链表,用来存储被接收方NAK包中返回的丢失包序号。这些数字以增加的顺序存储。

3.4.2.    数据发送算法
A. 如果发送端的丢失链表是非空的,重传第一个在list中的包,并删除该成员,到5。
B. 等待有应用程序数据需要发送
C. 如果未应答的包数量超过了两量窗口的大小,转到1。如果不是包装一个新的包并发送它。
D.如果当前包的序号是16n,n是一个整数,转第2步。
E. 在SND PKT历史窗口中记录包的发送时间
F. 如果这是自上次发送速率降低之后的第一个包,等外SYN时间。
G.等外(STP – t)时间,t是第1到第4步之间的总时间,然后转到1。


3.5. 接收端算法
3.5.1.    数据结构和变量
A. 接收端丢失链表:是一个duple连接链表,元素的值包括:丢失数据包的序号、最近丢失包的反馈时间和包已经被反馈的次数。值以包序号增序的方式存储。
B. 应答历史窗口:每个发送ACK的和时间一个循环数组;由于其循环的特性,意味着如果数组中没有更多空间的时候新的值将覆盖老的值。
C. RCV PKT历史窗口:一个用来记录每个包到达时间的循环数组。
D.对包窗口:一个用来记录每个探测包对之间的时间间隔。
E. LRSN:一个用来记录最大接收数据包需要的变量。LRSN被初始化为初始序号减1。

3.5.2.    数据接收算法
A. 查询系统时间来检查RC、ACK、NAK、或EXP定时器是否过期。如果任一定时器过期,处理事件(本节下面介绍)并复位过期的定时器。
B. 启动一个时间bounded UDP接收。如果每个包到,转1。
C. 设置exp-count为1,并更新ETP为:ETP=RTT+4*RTTVar + ATP。
D.如果所有的发送数据包已经被应答,复位EXP时间变量。
E. 检查包头的标志位。如果是一个控制包,根据类型处理它,然后转1。
F. 如果当前数据包的需要是16n+1,n是一个整数,记录当前包和上个在对包窗口中数据包的时间间隔。
G.在PKT历史窗口中记录包到达时间
H. 如果当前数据包的序号大于LRSN+1,将所有在(但不包括)这两个值之间的序号放入接收丢失链表,并在一个NAK包中将这些序号发送给发送端。如果序号小于LRSN,从接收丢失链表中删除它。
I.   更新LRSN,转1。

3.5.3.    RC定时器到
通过速率控制算法来更新STP(见3.6节)。

过程如下:
A. 按照下面的原则查找接收端所接收到的所有包之前的序号:如果接收者丢失链表是空的,ACK号码是LRSN+1,否则是在接收丢失队列中的最小序号。
B.如果应答号不大于曾经被ACK2应答的最大应答号,或等于上次应答的应答号并且两次应答之间的时间间隔小于RTT+4*RTTVar,停止(不发送应答)。
C. 分配这个应答一个唯一增加的ACK序列号,推荐采用ACK序列号按步骤1增加,并且重叠在达到最大值之后。
D.根据下面的算法来计算包的抵达速度:使用PKT历史窗口中的值计算最近16个包抵达间隔(AI)中值。在这16个值中,删除那些大于AI*8或小于AI*8的包,如果最后剩余8个值,计算他们的平均值(AI’),包抵达速度是1/AI’(每秒包的数量),否则是0。
E. 根据3.7节中的内容为每端(W)计算流量窗口。然后计算有效的流量窗口大小为:最大(W,可用接收方缓冲大小),2)。
F. 根据下面的算法来计算连接容量估计。如果流量控制快启动阶段(3.7)一直继续,返回0,否则计算最近16个对包间隔(PI),这些值在对包窗口中,那么连接容量就是1/PI(每秒包的数量)。
G.打包应答序列号,应答号,RTT,RTT 变量,有效的流量窗口大小并估计连接,将他们放入ACK包中,然后发送出去。
H. 记录ACK序列号,应答号和这个应答的开始时间,并放入历史窗口中。

3.5.4.    处理NAK定时器到时
?       查找接受方的丢失链表,找到所有上次反馈时间是(k*(RTT+4*RTTVar ) )前的包,k当前这个包的反馈次数加1,如果没有反馈丢失,停止。
?       压缩第一步中得到的序号(见3.9),然后在一个NAK包中发送他们到发送方。
?       如果不是停止流量控制快启动阶段。

3.5.5.    处理EXP定时器
A.   如果发送端的丢失链表不是空的,停止
B.   将所有未应答的包放到发送端的丢失链表中
C. 如果(exp-count>16)并且自上次从对方接收到一个包以来的总时间超过3秒,或者这个时间已经超过3分钟了,这被认为是连接已经断开,关闭UDT连接。
D.如果没有数据,也就没有应答,发送一个保活包给对端,否则将所有未应答包的序号放入发送丢失列表中。
E.   更新exp-count为:exp-count= exp-count+1
F.   更新ETP为:ETP=exp-count*(RTT+4*RTTVar)+ATP。

3.5.6.    收到应答包
A.   更新最大的应答序号
B. 更新RTT和RTTVar为:RTT = rtt, RTTVar = rv;rtt和rv是ACK包中的RTT和RTTVar值。
C.   更新NTP和ETP为:NTP=RTT+4*RTTVar;ETP=exp-count*(RTT+4*RTTVar)+ATP。
D. 更新连接容量估计:B=(B*7+b)/8,b是ACK包带的值。
E.   更新流量窗口大小为ACK中的值。
F.   发送ACK2包,并设置与ACK序号相同的应答号到对端
G. 复位EXP定时器

3.5.7.    当收到NAK包的时候
A. 将所有NAK包中带的序号放入发送方的丢失列表中
B. 通过速率控制来更新STP(见3.6)
C. 复位EXP定时器

3.5.8.    当收到ACK2包
?         在ACK历史窗口中根据接收到的ACK2序列号查找行营的ACK包。
?         更新曾经被应答的最大应答号
?       根据ACK2的到达时间和ACK离开时间计算新的rtt值,并且更新RTT和RTTVar值为:

RTTVar = (RTTVar *3 +abs(rtt-RTT)/4
RTT = (RTT *7+rtt)/8
RTT和RTTVar的初始值是0.1秒和0.05秒。

?       更新NTP和ETP为:
NTP = RTT;
ETP = (exp-count +1)* RTT+ATP
3.5.9. 当收到保活包的时候
什么也不做
3.5.10. 当收到连接握手和关闭包的时候
见3.8节

3.6. 速度控制算法
3.6.1.    速率控制快启动
STP被初始为最小的时间精度(1个CPU周期或1毫秒)。这是在快启动阶段,一般收到一个ACK包其携带的估计带宽大于0这个阶段就停止了。包的发送周期被设置为1/W,W是ACK携带的流量窗口的大小。
快启动阶段仅仅在开始一个UDT连接的时候发生,且不会在UDT连接的以后再出现。在快启动阶段之后,下面的算法就要工作了。

3.6.2.    当RC定时器时间到
1. 如果在上一个RCTP时间内,没有收到一个ACK,停止
2.计算在上个RCTP时间内的丢失率,计算方法是根据总共发送的包与NAK反馈中总共丢失包的数量。如果丢失率大于0.1%,停止。
3. 下个RCTP时间内发送包的增加数量如下计算:(inc)

If (B<=C) inc = 1/MSS
Else inc = max (10^(ceil(log10((B-C)*MSS*8)))*Beta/MSS,1/MSS)
B是连接容量估计,C是当前的发送速度。两个都计算为每秒多少个包。MSS是以字节计算的;Beta是值为0.0000015的常量。
4. 更新STP:STP=(STP*RCTP)/(STP*inc + RCTP)
5. 计算真正的数据发送周期(rsp),从SND PKT历史窗口中得到,如果(STP<0.5 *rsp)设置STP为(0.5 * rsp)。
6. 如果(STP<1.0),设置STP为1.0。

3.6.3.    当收到NAK包时
3.6.3.1.          数据结构和变量
1. LSD:自上次速率降低后发送的最大序号
2. NumNAK:自上次LSD更新以后的NAK数量
3. AvgNAK:当最大序号大于LSD时两次事件之间的NAK移动的平均数。
4. DR:在1到AvgNAK之间的随机平均数。

3.6.3.2.          算法
1. 如果NAK中最大的丢失序列号大于LSD:
增加STP为:STP=STP*(1+1/8)
更新AvgNAK为:AvgNAK = (AvgNAK *7 +NumNAK)/8
更新DR
复位 NumNAK = 0
记录LSD
2. 否则,增加NumNAK按照1个步骤增加;如果NumNAK % DR = 0;增加STP为:STP=STP*(1+1/8);记录LSD。
3.7. 流量控制算法
流量控制窗口大小(W)初始值是16。
3.7.1.    当ACK定时器到的时候
1. 流量控制快启动:如果没有NAK产生或者W没有到达或超过15个包,并且AS>0,流量窗口大小更新为应答包的总数量。
2. 否则,如果(AS>0),W更新为:(AS是包的到达速度)
W= ceil (W *0.875+AS* (RTT +ATP) *0.125)

3. 限制W到对方最大流量窗口大小。
3.8. 连接建立和关闭
一个UDT实体首先作为一个SERVER启动,当一个客户端需要连接的时候其发送握手包。客户端在从服务端接收到一个握手响应包或时间溢出之前,应该每隔一段时间发送一个握手包(时间间隔由响应时间和系统overhead来权衡)。

握手包有如下信息:

1. UDT版本:这个值是兼容的目的。当前的版本是2
2. 初始序号:这是发送这个UDT实体将来用于发送数据包的起始序号。它必须是一个在1到(2^31-1)之间的随机值。另外,建议这个值在合理的时间历史窗口中不应该重复。
3. MSS:数据包的大小(通过IP有效负载来度量)
4. 最大的流量窗口大小:这是接收到握手信息的UDT实体允许的最大流量窗口大小,窗口大小通常限制为接收端的数据结构大小。

服务器接收到一个握手包之后,比较MSS值和他自己的值并设置它自己的值为较小的值。结果值也在握手响应中被发送到客户端,另外还有服务器的版本信息,初始序列号,最大流量窗口大小。
版本字段用来检查两端的兼容性。初始序列号和最大流量窗口大小用于初始化接收到这个握手包的UDT实体参数。
服务器在第一步完成以后就准备发送或接收数据。然而,只要从同一个客户端接收任何握手包,其应该发送响应包。

客户端一旦得到服务器的一个握手响应其就进入发送和接收数据状态。设置它自己的MSS为握手响应包中的值并初始化相应的参数为包中的值(序列号、最大流量窗口)。如果收到任何其他的握手信息,丢掉它。
如果其中的UDT实体要关闭,它将发送一个关闭信息到对端;对方收到这个信息以后将自己关闭。这个关闭信息通过UDP传输,仅仅发送一次,并不保证一定收到。如果消息没有收到,对方将根据时间溢出机制来关闭连接。

3.9. 丢失信息的压缩方案
NAK包中携带的丢失信息是一个32-bit整数的数组。如果数组的中数字是一个正常的序号(第1位是0),这意味着这个序号的包丢失了,如果第1位是1,意味着从这个号码开始(包括该号码)到下一个数组中的元素(包括这个元素值)之间的包(它的第1位必须是0)都丢失。

例如,下面的NAK中携带的信息:
0x00000002, 0x80000006, 0x0000000B, 0x0000000E
上面的信息表明序号为:2,6,7,8,9,10,11,14的包都丢了。

TCP的滑动窗口协议是一种流量控制的协议,因为其设计的十分经典,今天重点讲一下其原理和实现。
窗口大小是与确认序号相对应的,确认序号是发送方等待接收的下一序号,确认已正确接收了所有序号小于等于确认号减1的数据字节。只有当报文首部的ACK标志置位时,确认序号才有效。
滑动窗口的左右边沿移动方式有如下几种:
1) 窗口合拢:窗口左边沿向右边沿靠近,当数据被发送和确认时有此现象。
2) 窗口张开:窗口右边缘向右移动时允许发送更多的数据,当接收进程读取已经确认的数据并释放TCP的接收缓存时有此现象。
3) 窗口收缩:窗口右边沿向左移动。
如果左边沿到达右边沿,称其为零窗口,此时发送方不能发送任何数据。
下图是滑动窗口在发送端的例子,

snd_wnd, snd_una, snd_nxt, snd_max都是TCP控制块中的字段。
snd_wnd是由接收方通知的提供窗口的大小,图例中从4到9,窗口大小为6。
snd_una为最小的未确认过的序列号,图例中为4。
snd_nxt为下一个发送序号,图例中为7。
snd_max为最大发送序号,主要用于重传,图例中为7。
由图中我们可以推断出,一个有效的ACK序号必须满足:snd_una < 确认序号<= snd_max。如果是重传的数据,下一个发送序号应该小于最大发送序号。

下图是滑动窗口在接收端的例子,如果接收报文段中携带的数据落在接收窗口内,该报文段是一个有效报文段。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值