UDT协议拥塞控制机制总结
重要名词:
SYN:DT increases its sending rate every 10 milliseconds, this constant time is defined as SYN.
SND:据包发送时间间隔,决定了数据包发送速率。
UDT允许用户访问两个拥塞控制参数:拥塞窗口大小和包与包之间的发送间隔。用户可以调整这两个参数来实现基于窗口的控制算法、基于速率的控制算法或者是一套混合的控制算法。
拥塞算法控制的核心变量:"拥塞窗口大小cwnd"和"包与包之间的发送间隔SND"。
控制时机:onACK,onLOSS,onTImeout,发生这些事件时更改上述2个变量。
分析思路:通过控制时机改变“拥塞窗口大小cwnd"和"包与包之间的发送间隔SND"这2个值,发包时通过这2个值决定发包行为。
拥塞过程:拥塞过程分为Slow start phase和regular phase阶段。 Slow start phase存在且仅存在于the beginning of a connection时。在slow start pha阶段, 数据包发送时间间隔 (SND) 为0. 拥塞窗口cwnd为2 ,且每次ACK到来时被设为the number of total sent packets so far。当收到NAK包或者达到最大cwnd窗口时 slow start阶段停止,SND = 1/ AS(arrival speed),进入regular阶段。
On ACK packet received:
1) If the current status is in the slow start phase, set the
congestion window size to the product of packet arrival rate and
(RTT + SYN). Slow Start ends. Stop.
2) Set the congestion window size (CWND) to: CWND = A * (RTT + SYN) +
16.
3) The number of sent packets to be increased in the next SYN period
(inc) is calculated as:
if (B <= C)
inc = 1/PS;
else
inc = max(10^(ceil(log10((B-C)*PS*8))) * Beta/PS, 1/PS);
where B is the estimated link capacity and C is the current
sending speed. All are counted as packets per second. PS is the
fixed size of UDT packet counted in bytes. Beta is a constant
value of 0.0000015.
4) The SND period is updated as:
SND = (SND * SYN) / (SND * inc + SYN).
On NAK packet received:
1) If it is in slow start phase, set inter-packet interval to
1/recvrate. Slow start ends. Stop.
2) If this NAK starts a new congestion period, increase inter-packet
interval (snd) to snd = snd * 1.125; Update AvgNAKNum, reset
NAKCount to 1, and compute DecRandom to a random (average
distribution) number between 1 and AvgNAKNum. Update LastDecSeq.
Stop.
3) If DecCount <= 5, and NAKCount == DecCount * DecRandom:
a. Update SND period: SND = SND * 1.125;
b. Increase DecCount by 1;
c. Record the current largest sent sequence number (LastDecSeq).
发包函数:
发送算法:
1)如果丢失列表不为空,则重传这些packet包,并从丢失列表中移出,到5)
2)若应用层有数据,则执行发送
3) 进行检查
a. 若未确认的包的数量超过流量窗口的大小,则回到1)
b. 组织一个新的数据包,之后发送出去
4) 若当前的包的序号是16n(16的倍数),回到2),此处的作用是pair packet,可用于估计带宽的
5) 记录包的发送时间到SND PKT History Window中
6)如果属于发送速率降低后发送的第一个包,则等待SYN的时间
7) 得到STP-t的时间, t是step 1 to step 4 花费的时间. 回到 1).
算法的核心:
(1)为了保证可靠性,没有发送成功的包,需要重传
(2)可以发送的包数量受拥塞窗口和滑动窗口的限制
(3)根据发送速率的控制,两个包之间的发送时间间隔需要控制
代码实现
long iterationStart;
public void senderAlgorithm()throws InterruptedException, IOException{
while(!paused){
// 本次迭代的时间
iterationStart=Util.getCurrentTime();
// 步骤1):if the sender's loss list is not empty
// 移出
Long entry=senderLossList.getFirstEntry();
if(entry!=null){
// 执行重传
handleRetransmit(entry);
}
else
{
// 步骤2) 若应用层有数据,则执行发送
// 步骤3) 进行检查
//if the number of unacknowledged data packets does not exceed the congestion
//and the flow window sizes, pack a new packet
int unAcknowledged=unacknowledged.get();
// 未确认的包的数量小于拥塞窗口大小和流控制窗口
if(unAcknowledged<session.getCongestionControl().getCongestionWindowSize()
&& unAcknowledged<session.getFlowWindowSize()){
//check for application data
DataPacket dp=flowWindow.consumeData();
if(dp!=null){
send(dp);
largestSentSequenceNumber=dp.getPacketSequenceNumber();
}
else{
statistics.incNumberOfMissingDataEvents();
}
}else{
//congestion window full, wait for an ack
if(unAcknowledged>=session.getCongestionControl().getCongestionWindowSize()){
statistics.incNumberOfCCWindowExceededEvents();
}
// udt-java自己加入的处理方式
waitForAck();
}
}
//wait
if(largestSentSequenceNumber % 16 !=0){
//发送时间间隔控制
long snd=(long)session.getCongestionControl().getSendInterval();
long passed=Util.getCurrentTime()-iterationStart;
int x=0;
while(snd-passed>0){
//can't wait with microsecond precision :(
if(x==0){
statistics.incNumberOfCCSlowDownEvents();
x++;
}
passed=Util.getCurrentTime()-iterationStart;
if(stopped)return;
}
}
}
}
拥塞控制算法实现
在UDT的实现中暴露了一些额外的参数给用户。用户自定义的拥塞控制算法中使用这些参数来调整包的发送速度。
下面的控制事件可以通过CCC进行重定义(例如,通过一个回调函数)
1)init:当UDT socket 建立连接时调用
2)close:当UDT socket关闭时调用
3)onACK:当收到ACK时调用
4)onLOSS:当收到NAK时调用
5)onTImeout:当定时器超时时调用
6)onPktSent:当发送数据包的时候调用
7)onPktRecv:当收到数据包的时候调用