UDT源码剖析(九)之CCC

这是整个UDT的算法核心:拥塞控制。我也没太搞明白,不过还是打算列出来,理解的不争取的地方,还请多多指正哈~

CCC

class CCC
{  
   virtual void init() {}    //在UDT connection建立起来的时候被调用
   virtual void close() {}   //在UDT connection关闭的时候被调用
   virtual void onACK(int32_t) {}   //在收到ACK的时候被调用
   virtual void onLoss(const int32_t*, int) {}    //一个丢失的报文被收到时调用
   virtual void onTimeout() {}       //超时的时候被调用
   virtual void onPktSent(const CPacket*) {}    //发送数据的时候被调用
   virtual void onPktReceived(const CPacket*) {}       //收到数据的时候被调用
   virtual void processCustomMsg(const CPacket*) {}       //在收到用户定义的packet时被调用

protected:    

   void setACKTimer(int msINT);   //设置周期性确认和ACK周期
   void setACKInterval(int pktINT);   //设置基于分组的确认以及发送ACK的分组数量
   void setRTO(int usRTO);   //设置RTO
   void sendCustomMsg(CPacket& pkt) const;   //发送用户定义的control packet
   const CPerfMon* getPerfInfo();   //检索性能信息
   void setUserParam(const char* param, int size);   //设置用户定义的参数

private:
   void setMSS(int mss);    //设置MSS
   void setMaxCWndSize(int cwnd);   //设置拥塞窗口
   void setBandwidth(int bw);   //设置带宽
   void setSndCurrSeqNo(int32_t seqno); //设置正确的seqno
   void setRcvRate(int rcvrate);    //设置recv rate
   void setRTT(int rtt);    //设置RTT

protected:

   const int32_t& m_iSYNInterval;       //SYN (SYN间隔数),每10ms增加计数
   double m_dPktSndPeriod;                //packet发送周期 ms
   double m_dCWndSize;                   //拥塞窗口大小,使用packet计数   
   int m_iBandwidth;               //估计的带宽,使用每秒发送的packet计数
   double m_dMaxCWndSize;               //最大的拥塞窗口,packet计数
   int m_iMSS;                 //最大的包大小
   int32_t m_iSndCurrSeqNo;        //当前最大的还没有发送的编号
   int m_iRcvRate;             //接收端的数据包的到达速率,使用每秒接受的packet计数
   int m_iRTT;             //RTT,使用ms计数
   char* m_pcParam;            //用户提供的参数
   int m_iPSize;               //用户提供的参数大小

private:

   UDTSOCKET m_UDT;          //UDT的索引            
   int m_iACKPeriod;                     //周期性定时器发送ACK,毫秒计数
   int m_iACKInterval;                   //多少个packet发送一个ACK,使用packets计数
   bool m_bUserDefinedRTO;                //RTO是自己算还是通过用户定义
   int m_iRTO;                            //RTO,ms计数
   CPerfMon m_PerfInfo;                    //统计信息
};
  • 初始化:CCC::CCC()

    CCC::CCC():
    m_iSYNInterval(CUDT::m_iSYNInterval),    /*每10ms增加一次*/
    m_dPktSndPeriod(1.0),     /*每1ms发送一个packet*/
    m_dCWndSize(16.0),     /*拥塞窗口的大小为16个packets*/
    m_iBandwidth(),     /*估计的带宽暂且不提*/
    m_dMaxCWndSize(),       /*最大的拥塞窗口也不提*/
    m_iMSS(),       /*MSS的值也不提*/
    m_iSndCurrSeqNo(),     /*当前最大的还没有发送的编号*/
    m_iRcvRate(),     /*接收端的接受速率*/
    m_iRTT(),       /*RTT*/
    m_pcParam(NULL),        /*假设用户没有提供参数*/
    m_iPSize(0),
    m_UDT(),        /*UDT的索引也没有提供*/
    m_iACKPeriod(0),        /*周期性发送ACK数值也没有提供*/
    m_iACKInterval(0),      /*多少packets发送一个ACK也没有提供*/
    m_bUserDefinedRTO(false),       /*默认计算RTO,不由用户提供*/
    m_iRTO(-1),     /*RTO默认为-1*/
    m_PerfInfo()        /*全局统计变量默认不提供*/
    {
    }
  • 销毁:CCC::~CCC()

    CCC::~CCC()
    {
       delete [] m_pcParam; //删除用户提供的参数就行了
    }
  • 发送用户定义的packets:void CCC::sendCustomMsg(CPacket& pkt) const
    ```
    void CCC::sendCustomMsg(CPacket& pkt) const
    {
    CUDT* u = CUDT::getUDTHandle(m_UDT);

if (NULL != u)
{
pkt.m_iID = u->m_PeerID;
u->m_pSndQueue->sendto(u->m_pPeerAddr, pkt);
}
}

- 向CUDT实例填充统计信息:`const CPerfMon* CCC::getPerfInfo()`

const CPerfMon* CCC::getPerfInfo()
{
try
{
CUDT* u = CUDT::getUDTHandle(m_UDT);
if (NULL != u)
u->sample(&m_PerfInfo, false);
}
catch (...)
{
return NULL;
}

return &m_PerfInfo;
}

- 设置系列函数:

void CCC::setACKTimer(int msINT) //设置周期性确认和ACK周期(最大为10ms,不可能超过10ms)
{
m_iACKPeriod = msINT > m_iSYNInterval ? m_iSYNInterval : msINT;
}

void CCC::setACKInterval(int pktINT) //设置多少个packets发送一个ACK
{
m_iACKInterval = pktINT;
}

void CCC::setRTO(int usRTO) //使用用户提供的RTO估算
{
m_bUserDefinedRTO = true;
m_iRTO = usRTO;
}
void CCC::setMSS(int mss) //设置MSS

{
m_iMSS = mss;
}

扫描二维码关注公众号,回复: 1613022 查看本文章

void CCC::setBandwidth(int bw) //设置带宽
{
m_iBandwidth = bw;
}

void CCC::setSndCurrSeqNo(int32_t seqno) //设置当前的seqno
{
m_iSndCurrSeqNo = seqno;
}

void CCC::setRcvRate(int rcvrate) //设置接收rate
{
m_iRcvRate = rcvrate;
}

void CCC::setMaxCWndSize(int cwnd) //设置最大的拥塞窗口的数值
{
m_dMaxCWndSize = cwnd;
}

void CCC::setRTT(int rtt) //设置RTT
{
m_iRTT = rtt;
}

void CCC::setUserParam(const char* param, int size) //设置用户参数
{
delete [] m_pcParam;
m_pcParam = new char[size];
memcpy(m_pcParam, param, size);
m_iPSize = size;
}

##CCCFactory

class CCCVirtualFactory
{
public:
virtual ~CCCVirtualFactory() {}

virtual CCC* create() = 0;
virtual CCCVirtualFactory* clone() = 0;
};

template

virtual CCC* create() {return new T;}
virtual CCCVirtualFactory* clone() {return new CCCFactory

##CUDTCC

class CUDTCC: public CCC
{
public:
CUDTCC();

public:
virtual void init(); //继承的函数
virtual void onACK(int32_t);
virtual void onLoss(const int32_t*, int);
virtual void onTimeout();

private:
int m_iRCInterval; //UDT速率控制间隔
uint64_t m_LastRCTime; //最后的速率增加时间
bool m_bSlowStart; //是否处于慢启动阶段
int32_t m_iLastAck; //最后收到的ACKed seqno
bool m_bLoss; //是否从上次增加速率之后发生的丢失事件
int32_t m_iLastDecSeq; //最大的packet seqno,自从最后一次减少速率
double m_dLastDecPeriod; //最后一次减少速率时发送packet的速率
int m_iNAKCount; //收到NAK包的计数
int m_iDecRandom; //发生随机的阀值下降
int m_iAvgNAKNum; //在拥塞期间收到的平均的NAK数值
int m_iDecCount; //拥塞期间减少的数量
};

- 初始化:`CUDTCC::CUDTCC()`

CUDTCC::CUDTCC():
m_iRCInterval(),
m_LastRCTime(),
m_bSlowStart(),
m_iLastAck(),
m_bLoss(),
m_iLastDecSeq(),
m_dLastDecPeriod(),
m_iNAKCount(),
m_iDecRandom(),
m_iAvgNAKNum(),
m_iDecCount()
{
}

- 使用时初始化:`void CUDTCC::init()`

void CUDTCC::init()
{
m_iRCInterval = m_iSYNInterval; //UDT的速率控制间隔定义为10ms
m_LastRCTime = CTimer::getTime(); //最后的速率增加时间定义为现在
setACKTimer(m_iRCInterval); //设置周期性确认的ACK为10ms

m_bSlowStart = true; //启动时处于慢启动阶段
m_iLastAck = m_iSndCurrSeqNo; //最后一次收到的ACK是当前还没有发送的编号
m_bLoss = false; //刚开始没有出现loss事件
m_iLastDecSeq = CSeqNo::decseq(m_iLastAck); //最大发送seqno为最大的需要发送的seqno-1
m_dLastDecPeriod = 1; //刚开始的速率为10ms/1 packet
m_iAvgNAKNum = 0; //收到的NAK计数为0
m_iNAKCount = 0;
m_iDecRandom = 1; //随机的阀值下降为0

m_dCWndSize = 16; //拥塞窗口的大小为16
m_dPktSndPeriod = 1; //packet的发送时间周期为10ms/1 packet
}

- 收到一个ACK时:`void CUDTCC::onACK(int32_t ack)`

void CUDTCC::onACK(int32_t ack)
{
int64_t B = 0;
double inc = 0;
// Note: 1/24/2012
// 最小增加参数从“1.0 / m_iMSS”增加到0.01,因为原稿太小,导致发送速率在长时间内保持在低水平
const double min_inc = 0.01;

uint64_t currtime = CTimer::getTime(); //获取当前的时间
if (currtime - m_LastRCTime < (uint64_t)m_iRCInterval) //如果收到的ACK的时间<10ms,视为正常,不进行处理
return;

m_LastRCTime = currtime; //否则的话,就超时了哇

if (m_bSlowStart) //如果此时位于慢启动阶段
{
m_dCWndSize += CSeqNo::seqlen(m_iLastAck, ack);//收到了几个ACK就增加几个拥塞窗口(packets计数),和TCP的策略一样
m_iLastAck = ack; //更新上一次收到的ACK

  if (m_dCWndSize > m_dMaxCWndSize) //是不是可以把这个当做慢启动门限来翻译啊?
  { //如果现在的拥塞窗口大于满启动门限
     m_bSlowStart = false;  //关闭慢启动
     if (m_iRcvRate > 0)    //如果接受方的速率>0
        m_dPktSndPeriod = 1000000.0 / m_iRcvRate;//将发送周期与接受周期(速率)相对应
     else//否则,减小发送速率,具体=(拥塞窗口packets/(RTT+10ms))
        m_dPktSndPeriod = m_dCWndSize / (m_iRTT + m_iRCInterval);
  }

}
else//处于拥塞避免阶段,拥塞窗口的增加程度变小
m_dCWndSize = m_iRcvRate / 1000000.0 * (m_iRTT + m_iRCInterval) + 16;

// During Slow Start, no rate increase
if (m_bSlowStart) //如果处于慢启动阶段,增加完拥塞窗口就可以返回了
return;

//此后的处理,处于拥塞避免阶段
if (m_bLoss) //上一次是否有丢失事件发生,此时已经离开了慢启动阶段
{ //上一次因为丢包而引起的重传而进入慢启动阶段的标志loss清除掉
m_bLoss = false;
return; //然后返回
}

//之后是对拥塞避免阶段的一些处理

B = (int64_t)(m_iBandwidth - 1000000.0 / m_dPktSndPeriod);//每秒估计发送的packets - 每秒实际发送的packets
//如果每秒的发送速率大于最后减少时的发送速率(比上一次发生拥塞时的速率还要高)&&
//每秒估计发送的packets/9 < 估计与实际的发送时间之差
if ((m_dPktSndPeriod > m_dLastDecPeriod) && ((m_iBandwidth / 9) < B))
B = m_iBandwidth / 9; //调整B为估计发送速率的1/9,控制B的数值
if (B <= 0)
inc = min_inc;//如果发送速率还没有达到之前一次的顶峰,inc=0.01
else
{
// inc = max(10 ^ ceil(log10( B * MSS * 8 ) * Beta / MSS, 1/MSS)
// Beta = 1.5 * 10^(-6)

  //否则一顿蛇皮计算 
  inc = pow(10.0, ceil(log10(B * m_iMSS * 8.0))) * 0.0000015 / m_iMSS;
    
  //确定无论如何计算,inc<=0.01
  if (inc < min_inc)
     inc = min_inc;

}

//调整当前的发送速率为:发送速率10 / (发送速率0.01+10)
//确保不会发送速率不会超过上一次发生拥塞时的速率,防止再次出现丢包事件
m_dPktSndPeriod = (m_dPktSndPeriod * m_iRCInterval) / (m_dPktSndPeriod * inc + m_iRCInterval);
}

- 在发生包丢失事件时被调用:`void CUDTCC::onLoss(const int32_t* losslist, int)`

void CUDTCC::onLoss(const int32_t* losslist, int)
{
//Slow Start stopped, if it hasn't yet
if (m_bSlowStart) //如果处于慢启动状态
{
m_bSlowStart = false; //在慢启动阶段丢包,退出慢启动状态,免得拥塞窗口暴跌
if (m_iRcvRate > 0) //对方接收的速率
{
// Set the sending rate to the receiving rate.
m_dPktSndPeriod = 1000000.0 / m_iRcvRate; //调整发送速率
return;
}
// If no receiving rate is observed, we have to compute the sending
// rate according to the current window size, and decrease it
// using the method below.
m_dPktSndPeriod = m_dCWndSize / (m_iRTT + m_iRCInterval);//适当的降低发送速率,反正不会再次进入满启动,导致拥塞窗口暴跌
}

//没有在慢启动状态丢失packet或者对方的接受速率正常
m_bLoss = true; //将丢包设置为true

//比较之前还有丢包没有处理
if (CSeqNo::seqcmp(losslist[0] & 0x7FFFFFFF, m_iLastDecSeq) > 0)
{
m_dLastDecPeriod = m_dPktSndPeriod; //将上一次发送包的速率调增为现在的速率
m_dPktSndPeriod = ceil(m_dPktSndPeriod * 1.125);//增减发送速率??不应该降低吗??此时处于拥塞避免期间

  //调整丢失包的NAK NUMBER
  m_iAvgNAKNum = (int)ceil(m_iAvgNAKNum * 0.875 + m_iNAKCount * 0.125);
  m_iNAKCount = 1;  //设计拥塞期间的数据
  m_iDecCount = 1;

  m_iLastDecSeq = m_iSndCurrSeqNo;  //调整丢失的包的ACK

  // remove global synchronization using randomization
  //使用随机化去除全局同步,没有搞懂他是想干啥 
  srand(m_iLastDecSeq); 
  m_iDecRandom = (int)ceil(m_iAvgNAKNum * (double(rand()) / RAND_MAX));
  if (m_iDecRandom < 1)
     m_iDecRandom = 1;

}
else if ((m_iDecCount ++ < 5) && (0 == (++ m_iNAKCount % m_iDecRandom)))
{
// 0.875^5 = 0.51, rate should not be decreased by more than half within a congestion period
m_dPktSndPeriod = ceil(m_dPktSndPeriod * 1.125); //反正就是根据接收方接收的速率,调整本地的发送速率
m_iLastDecSeq = m_iSndCurrSeqNo;
}
}

- 超时时被调用:`void CUDTCC::onTimeout()`

//在超时的时候,进行计算
//在超时的时候处于慢启动阶段,但是依旧发生了丢包,那么增加速率再次降低
//在TCP中,若在慢启动阶段发生丢包时,会重新进入慢启动阶段,拥塞窗口调整为1,降低慢启动门限
void CUDTCC::onTimeout()
{
if (m_bSlowStart) //如果处于慢启动阶段,不过为啥要退出慢启动阶段??
{
m_bSlowStart = false; //退出慢启动阶段
if (m_iRcvRate > 0) //如果接收方的速率>0
m_dPktSndPeriod = 1000000.0 / m_iRcvRate; //调整发送方的速率与接受方相同
else //如果接收方的速率<0,计算发送方的速率为:拥塞窗口/(RTT+10),减低发送速率
m_dPktSndPeriod = m_dCWndSize / (m_iRTT + m_iRCInterval);
}
else
{
/
m_dLastDecPeriod = m_dPktSndPeriod;
m_dPktSndPeriod = ceil(m_dPktSndPeriod
2);
m_iLastDecSeq = m_iLastAck;
*/
}
}
```

猜你喜欢

转载自www.cnblogs.com/ukernel/p/9191066.html
ccc
今日推荐