UDT主要通过在数据收发的过程中进行精细的控制来实现对于网络带宽更加有效的利用,并使网络中数据传输的速率尽可能快。
如我们前面在分析数据发送的控制中看到的,对于正常的顺序packet发送,发送控制主要在于两个方面,一是发送窗口的大小,也就是某个时刻已经发送但未得到相应的packet的最大个数,这一点主要由拥塞窗口大小m_dCongestionWindow和滑动窗口大小m_iFlowWindowSize来描述,发送窗口大小为两者中较小的那一个;二是控制两个数据包发送的时间间隔,也就是包的发送速率,这一点则主要用数据包发送时间间隔m_ullInterval来描述。所有的发送控制机制主要通过影响这几个变量来控制发送过程。
发送窗口大小对于数据packet发送过程的影响比较直接,在CUDT::packData(CPacket& packet, uint64_t& ts)中,会检查最后被ACK的数据packet的SeqNo m_iSndLastAck到最近发送的数据packet的SeqNo m_iSndCurrSeqNo的offset,若offset大于窗口大小,就不再发送数据。
发送送率的控制则略微复杂一点。在CUDT::packData(CPacket& packet, uint64_t& ts)中会计算下一个数据packet发送的理想的时间点,记录在m_ullTargetTime中用于track及调整后续数据packet的发送时间,并会将该时间值返回给调用者CSndUList::pop(),CSndUList::pop()则会在将CUDT重新插入发送列表时更新CUDT的CSNode m_pSNode的时间戳字段,并根据新的时间戳来讲CUDT放在CSndUList的CUDT堆的适当位置上。
这里就来更细致地看一下UDT中发送窗口大小及发送速率的调整。
滑动窗口大小m_iFlowWindowSize
对于m_iFlowWindowSize,搜遍UDT的整个code,可以看到,主要在这样的几个地方会去更新它:
1. 数据接收端和数据发送端在建立连接的过程中会通过HandShake消息协商确认该值。
CUDT::connect(const CPacket& response):
m_iFlowWindowSize = m_ConnRes.m_iFlightFlagSize;
和CUDT::connect(const sockaddr* peer, CHandShake* hs):
1// exchange info for maximum flow window size 2 m_iFlowWindowSize = hs->m_iFlightFlagSize;
CUDT::connect(const sockaddr* serv_addr)中连接握手请求m_ConnReq的m_iFlightFlagSize:
m_ConnReq.m_iFlightFlagSize = (m_iRcvBufSize < m_iFlightFlagSize) ? m_iRcvBufSize : m_iFlightFlagSize;
在CUDT::CUDT()中会初始化m_iFlightFlagSize和m_iRcvBufSize:
1m_iFlightFlagSize = 25600; 2 m_iSndBufSize = 8192; 3 m_iRcvBufSize = 8192; //Rcv buffer MUST NOT be bigger than Flight Flag size
m_iFlightFlagSize和m_iRcvBufSize这两个选项还可以通过UDT::setsockopt()进行设置(src/core.cpp):
1void CUDT::setOpt(UDTOpt optName, const void* optval, int) { 2 if (m_bBroken || m_bClosing) 3 throw CUDTException(2, 1, 0); 4 5 CGuard cg(m_ConnectionLock); 6 CGuard sendguard(m_SendLock); 7 CGuard recvguard(m_RecvLock); 8 9 switch (optName) { 10 11 12 case UDT_FC: 13 if (m_bConnecting || m_bConnected) 14 throw CUDTException(5, 2, 0); 15 16 if (*(int*) optval < 1) 17 throw CUDTException(5, 3); 18 19 // Mimimum recv flight flag size is 32 packets 20 if (*(int*) optval > 32) 21 m_iFlightFlagSize = *(int*) optval; 22 else 23 m_iFlightFlagSize = 32; 24 25 break; 26 27 28 case UDT_RCVBUF: 29 if (m_bOpened) 30 throw CUDTException(5, 1, 0); 31 32 if (*(int*) optval <= 0) 33 throw CUDTException(5, 3, 0); 34 35 // Mimimum recv buffer size is 32 packets 36 if (*(int*) optval > (m_iMSS - 28) * 32) 37 m_iRcvBufSize = *(int*) optval / (m_iMSS - 28); 38 else 39 m_iRcvBufSize = 32; 40 41 // recv buffer MUST not be greater than FC size 42 if (m_iRcvBufSize > m_iFlightFlagSize) 43 m_iRcvBufSize = m_iFlightFlagSize; 44 45 break;
2. 数据接收端发送的“light” ACK消息减小m_iFlowWindowSize。
如CUDT::processCtrl(CPacket& ctrlpkt)中这样的一段code:
1// process a lite ACK 2 if (4 == ctrlpkt.getLength()) { 3 ack = *(int32_t *) ctrlpkt.m_pcData; 4 if (CSeqNo::seqcmp(ack, m_iSndLastAck) >= 0) { 5 m_iFlowWindowSize -= CSeqNo::seqoff(m_iSndLastAck, ack); 6 m_iSndLastAck = ack; 7 } 8 9 break; 10 }
如我们前面在 UDT数据收发的可靠性保障 的ACK部分看到的那样,“light” ACK消息通常是由于在某一小段时间内突然到达了大量的数据packet,才会发送的,这通常表明发送端发送数据过快过多了,因而“light” ACK消息会减小数据发送端的滑动窗口大小m_iFlowWindowSize。
3. 数据接收端发送的常规ACK消息直接设置发送端的滑动窗口大小m_iFlowWindowSize。
如CUDT::processCtrl(CPacket& ctrlpkt)中这样的一段code:
1if (CSeqNo::seqcmp(ack, m_iSndLastAck) >= 0) { 2 // Update Flow Window Size, must update before and together with m_iSndLastAck 3 m_iFlowWindowSize = *((int32_t *) ctrlpkt.m_pcData + 3); 4 m_iSndLastAck = ack; 5 }
而数据接收端则主要根据它自己的接收缓冲区的可用大小来设置,如CUDT::sendCtrl()中的这段code:
1data[3] = m_pRcvBuffer->getAvailBufSize(); 2 // a minimum flow window of 2 is used, even if buffer is full, to break potential deadlock 3 if (data[3] < 2) 4 data[3] = 2; 5 6 if (currt
更新m_iFlowWindowSize的地方基本上就是这3个。
m_iFlowWindowSize除了会在CUDT::packData()中被用来决定发送窗口的大小之外,它的初始值还决定着发送丢失列表的大小,如CUDT::connect()中:
1// after introducing lite ACK, the sndlosslist may not be cleared in time, so it requires twice space. 2 m_pSndLossList = new CSndLossList(m_iFlowWindowSize * 2); 3 m_pRcvLossList = new CRcvLossList(m_iFlightFlagSize);
及拥塞控制器最大的拥塞窗口的大小,如CUDT::connect()中:
m_pCC->setMaxCWndSize(m_iFlowWindowSize);
UDT中与滑动窗口大小相关的内容基本上就是这些。
数据包发送时间间隔m_ullInterval
对于m_ullInterval,在UDT中,则有如下的的几个地方会去更新它:
1. 数据接收端和数据发送端在建立连接过程中的CUDT::connect(const CPacket& response)和CUDT::connect(const sockaddr* peer, CHandShake* hs)中会根据拥塞控制器的m_dPktSndPeriod的初始值计算该值:
m_ullInterval = (uint64_t) (m_pCC->m_dPktSndPeriod * m_ullCPUFrequency);
UDT默认的拥塞控制器是将m_dPktSndPeriod初始化为1.0的(src/ccc.cpp):
1CCC::CCC() 2 : m_iSYNInterval(CUDT::m_iSYNInterval), 3 m_dPktSndPeriod(1.0), 4...... 5 6void CUDTCC::init() { 7...... 8 9 m_dCWndSize = 16; 10 m_dPktSndPeriod = 1; 11}
2. 将拥塞控制器中的计算成果同步进CUDT中时会更新m_ullInterval。可以看下CUDT::CCUpdate():
1void CUDT::CCUpdate() { 2 m_ullInterval = (uint64_t) (m_pCC->m_dPktSndPeriod * m_ullCPUFrequency); 3 m_dCongestionWindow = m_pCC->m_dCWndSize; 4 5 if (m_llMaxBW <= 0) 6 return; 7 const double minSP = 1000000.0 / (double(m_llMaxBW) / m_iMSS) * m_ullCPUFrequency; 8 if (m_ullInterval < minSP) 9 m_ullInterval = minSP; 10}
这里同样是根据拥塞控制器的m_dPktSndPeriod值计算该值,计算方法也与前一种相同。但m_ullInterval的实际取值会受限与最大数据传输率m_llMaxBW的值。最大数据传输率m_llMaxBW默认为无效值-1,CUDT::CUDT()中:
m_llMaxBW = -1;
但可以通过UDT::setsockopt()进行设置(src/core.cpp):
1case UDT_MAXBW: 2 m_llMaxBW = *(int64_t*) optval; 3 break;
3. 数据接收端反馈的DelayWarning控制消息增加m_ullInterval。在CUDT::processCtrl()中可以看到对于DelayWarning消息的处理:
1case 4: //100 - Delay Warning 2 // One way packet delay is increasing, so decrease the sending rate 3 m_ullInterval = (uint64_t) ceil(m_ullInterval * 1.125); 4 m_iLastDecSeq = m_iSndCurrSeqNo; 5 6 break;
CUDT::sendCtrl()中发送DelayWarning的过程:
1case 4: //100 - Congestion Warning 2 ctrlpkt.pack(pkttype); 3 ctrlpkt.m_iID = m_PeerID; 4 m_pSndQueue->sendto(m_pPeerAddr, ctrlpkt); 5 6 CTimer::rdtsc(m_ullLastWarningTime); 7 8 break;
这个过程看上去蛮直接的。但在4.0版本的UDT中,DelayWarning/CongestiongWarning消息实际上不再被用到了。在CUDT::processCtrl()中处理ACK2的case中可以看到被注释掉的这行code:
1//if increasing delay detected... 2 // sendCtrl(4);
只能缅怀曾经在发挥作用的 DelayWarning /CongestiongWarning消息 了。
如此看来,则数据包发送时间间隔m_ullInterval似乎总是由拥塞控制器的m_dPktSndPeriod在决定了。
拥塞控制器
UDT使用拥塞控制器来追踪网络数据传输过程中发生的事件,如接收到了ACK,发生了超时等,并根据这些事件产生的时机及其它的一些基本设置进行计算,用计算结果来控制ACK消息发送的频率,发送窗口的大小,及数据包的发送频率等。
拥塞控制器虚拟工厂CCCVirtualFactory
UDT的拥塞控制器机制中,不直接创建拥塞控制器,而是通过拥塞控制器工厂来创建。可以看一下拥塞控制器工厂接口CCCVirtualFactory及默认的拥塞控制器工厂实现CCCFactory的定义:
1class CCCVirtualFactory { 2 public: 3 virtual ~CCCVirtualFactory() { 4 } 5 6 virtual CCC* create() = 0; 7 virtual CCCVirtualFactory* clone() = 0; 8}; 9 10template<class T> 11class CCCFactory : public CCCVirtualFactory { 12 public: 13 virtual ~CCCFactory() { 14 } 15 16 virtual CCC* create() { 17 return new T; 18 } 19 virtual CCCVirtualFactory* clone() { 20 return new CCCFactory<T> ; 21 } 22};
CCCFactory主要会被用于创建默认拥塞控制器CUDTCC(src/core.cpp):
1CUDT::CUDT() { 2...... 3 m_pCCFactory = new CCCFactory<CUDTCC>;
要实现自定义的拥塞控制策略,也是需要实现自己的拥塞控制器虚拟工厂接口CCCVirtualFactory,令自定义的拥塞控制器虚拟工厂创建自定义的拥塞控制器,并将自定义的拥塞控制器虚拟工厂设置给UDT。在CUDT::setOpt()中可见:
1case UDT_CC: 2 if (m_bConnecting || m_bConnected) 3 throw CUDTException(5, 1, 0); 4 if (NULL != m_pCCFactory) 5 delete m_pCCFactory; 6 m_pCCFactory = ((CCCVirtualFactory *) optval)->clone(); 7 8 break;
顺便提一下,在CUDT::getOpt()中获取UDT_CC选项的值则是直接获取的拥塞控制器而不是拥塞控制器虚拟工厂:
1void CUDT::getOpt(UDTOpt optName, void* optval, int& optlen) { 2 CGuard cg(m_ConnectionLock); 3。。。。。。 4 5 case UDT_CC: 6 if (!m_bOpened) 7 throw CUDTException(5, 5, 0); 8 *(CCC**) optval = m_pCC; 9 optlen = sizeof(CCC*); 10 11 break;
UDT中拥塞控制器虚拟工厂的内容基本上就是这些了。
拥塞控制器CCC
这里再来看一下拥塞控制CCC的定义(src/ccc.h):
1class UDT_API CCC { 2 friend class CUDT; 3 4 public: 5 CCC(); 6 virtual ~CCC(); 7 8 private: 9 CCC(const CCC&); 10 CCC& operator=(const CCC&) { 11 return *this; 12 } 13 14 public: 15 16 // Functionality: 17 // Callback function to be called (only) at the start of a UDT connection. 18 // note that this is different from CCC(), which is always called. 19 // Parameters: 20 // None. 21 // Returned value: 22 // None. 23 24 virtual void init() { 25 } 26 27 // Functionality: 28 // Callback function to be called when a UDT connection is closed. 29 // Parameters: 30 // None. 31 // Returned value: 32 // None. 33 34 virtual void close() { 35 } 36 37 // Functionality: 38 // Callback function to be called when an ACK packet is received. 39 // Parameters: 40 // 0) [in] ackno: the data sequence number acknowledged by this ACK. 41 // Returned value: 42 // None. 43 44 virtual void onACK(int32_t) { 45 } 46 47 // Functionality: 48 // Callback function to be called when a loss report is received. 49 // Parameters: 50 // 0) [in] losslist: list of sequence number of packets, in the format describled in packet.cpp. 51 // 1) [in] size: length of the loss list. 52 // Returned value: 53 // None. 54 55 virtual void onLoss(const int32_t*, int) { 56 } 57 58 // Functionality: 59 // Callback function to be called when a timeout event occurs. 60 // Parameters: 61 // None. 62 // Returned value: 63 // None. 64 65 virtual void onTimeout() { 66 } 67 68 // Functionality: 69 // Callback function to be called when a data is sent. 70 // Parameters: 71 // 0) [in] seqno: the data sequence number. 72 // 1) [in] size: the payload size. 73 // Returned value: 74 // None. 75 76 virtual void onPktSent(const CPacket*) { 77 } 78 79 // Functionality: 80 // Callback function to be called when a data is received. 81 // Parameters: 82 // 0) [in] seqno: the data sequence number. 83 // 1) [in] size: the payload size. 84 // Returned value: 85 // None. 86 87 virtual void onPktReceived(const CPacket*) { 88 } 89 90 // Functionality: 91 // Callback function to Process a user defined packet. 92 // Parameters: 93 // 0) [in] pkt: the user defined packet. 94 // Returned value: 95 // None. 96 97 virtual void processCustomMsg(const CPacket*) { 98 } 99 100 protected: 101 102 // Functionality: 103 // Set periodical acknowldging and the ACK period. 104 // Parameters: 105 // 0) [in] msINT: the period to send an ACK. 106 // Returned value: 107 // None. 108 109 void setACKTimer(int msINT); 110 111 // Functionality: 112 // Set packet-based acknowldging and the number of packets to send an ACK. 113 // Parameters: 114 // 0) [in] pktINT: the number of packets to send an ACK. 115 // Returned value: 116 // None. 117 118 void setACKInterval(int pktINT); 119 120 // Functionality: 121 // Set RTO value. 122 // Parameters: 123 // 0) [in] msRTO: RTO in macroseconds. 124 // Returned value: 125 // None. 126 127 void setRTO(int usRTO); 128 129 // Functionality: 130 // Send a user defined control packet. 131 // Parameters: 132 // 0) [in] pkt: user defined packet. 133 // Returned value: 134 // None. 135 136 void sendCustomMsg(CPacket& pkt) const; 137 138 // Functionality: 139 // retrieve performance information. 140 // Parameters: 141 // None. 142 // Returned value: 143 // Pointer to a performance info structure. 144 145 const CPerfMon* getPerfInfo(); 146 147 // Functionality: 148 // Set user defined parameters. 149 // Parameters: 150 // 0) [in] param: the paramters in one buffer. 151 // 1) [in] size: the size of the buffer. 152 // Returned value: 153 // None. 154 155 void setUserParam(const char* param, int size); 156 157 private: 158 void setMSS(int mss); 159 void setMaxCWndSize(int cwnd); 160 void setBandwidth(int bw); 161 void setSndCurrSeqNo(int32_t seqno); 162 void setRcvRate(int rcvrate); 163 void setRTT(int rtt); 164 165 protected: 166 const int32_t& m_iSYNInterval; // UDT constant parameter, SYN 167 168 double m_dPktSndPeriod; // Packet sending period, in microseconds 169 double m_dCWndSize; // Congestion window size, in packets 170 171 int m_iBandwidth; // estimated bandwidth, packets per second 172 double m_dMaxCWndSize; // maximum cwnd size, in packets 173 174 int m_iMSS; // Maximum Packet Size, including all packet headers 175 int32_t m_iSndCurrSeqNo; // current maximum seq no sent out 176 int m_iRcvRate; // packet arrive rate at receiver side, packets per second 177 int m_iRTT; // current estimated RTT, microsecond 178 179 char* m_pcParam; // user defined parameter 180 int m_iPSize; // size of m_pcParam 181 182 private: 183 UDTSOCKET m_UDT; // The UDT entity that this congestion control algorithm is bound to 184 185 int m_iACKPeriod; // Periodical timer to send an ACK, in milliseconds 186 int m_iACKInterval; // How many packets to send one ACK, in packets 187 188 bool m_bUserDefinedRTO; // if the RTO value is defined by users 189 int m_iRTO; // RTO value, microseconds 190 191 CPerfMon m_PerfInfo; // protocol statistics information 192};
然后是CCC的实现(src/ccc.cpp):
1CCC::CCC() 2 : m_iSYNInterval(CUDT::m_iSYNInterval), 3 m_dPktSndPeriod(1.0), 4 m_dCWndSize(16.0), 5 m_iBandwidth(), 6 m_dMaxCWndSize(), 7 m_iMSS(), 8 m_iSndCurrSeqNo(), 9 m_iRcvRate(), 10 m_iRTT(), 11 m_pcParam(NULL), 12 m_iPSize(0), 13 m_UDT(), 14 m_iACKPeriod(0), 15 m_iACKInterval(0), 16 m_bUserDefinedRTO(false), 17 m_iRTO(-1), 18 m_PerfInfo() { 19} 20 21CCC::~CCC() { 22 delete[] m_pcParam; 23} 24 25void CCC::setACKTimer(int msINT) { 26 m_iACKPeriod = msINT > m_iSYNInterval ? m_iSYNInterval : msINT; 27} 28 29void CCC::setACKInterval(int pktINT) { 30 m_iACKInterval = pktINT; 31} 32 33void CCC::setRTO(int usRTO) { 34 m_bUserDefinedRTO = true; 35 m_iRTO = usRTO; 36} 37 38void CCC::sendCustomMsg(CPacket& pkt) const { 39 CUDT* u = CUDT::getUDTHandle(m_UDT); 40 41 if (NULL != u) { 42 pkt.m_iID = u->m_PeerID; 43 u->m_pSndQueue->sendto(u->m_pPeerAddr, pkt); 44 } 45} 46 47const CPerfMon* CCC::getPerfInfo() { 48 try { 49 CUDT* u = CUDT::getUDTHandle(m_UDT); 50 if (NULL != u) 51 u->sample(&m_PerfInfo, false); 52 } catch (...) { 53 return NULL; 54 } 55 56 return &m_PerfInfo; 57} 58 59void CCC::setMSS(int mss) { 60 m_iMSS = mss; 61} 62 63void CCC::setBandwidth(int bw) { 64 m_iBandwidth = bw; 65} 66 67void CCC::setSndCurrSeqNo(int32_t seqno) { 68 m_iSndCurrSeqNo = seqno; 69} 70 71void CCC::setRcvRate(int rcvrate) { 72 m_iRcvRate = rcvrate; 73} 74 75void CCC::setMaxCWndSize(int cwnd) { 76 m_dMaxCWndSize = cwnd; 77} 78 79void CCC::setRTT(int rtt) { 80 m_iRTT = rtt; 81} 82 83void CCC::setUserParam(const char* param, int size) { 84 delete[] m_pcParam; 85 m_pcParam = new char[size]; 86 memcpy(m_pcParam, param, size); 87 m_iPSize = size; 88}
然后是UDT中默认的CCC CUDTCC定义:
1class CUDTCC : public CCC { 2 public: 3 CUDTCC(); 4 5 public: 6 virtual void init(); 7 virtual void onACK(int32_t); 8 virtual void onLoss(const int32_t*, int); 9 virtual void onTimeout(); 10 11 private: 12 int m_iRCInterval; // UDT Rate control interval 13 uint64_t m_LastRCTime; // last rate increase time 14 bool m_bSlowStart; // if in slow start phase 15 int32_t m_iLastAck; // last ACKed seq no 16 bool m_bLoss; // if loss happened since last rate increase 17 int32_t m_iLastDecSeq; // max pkt seq no sent out when last decrease happened 18 double m_dLastDecPeriod; // value of pktsndperiod when last decrease happened 19 int m_iNAKCount; // NAK counter 20 int m_iDecRandom; // random threshold on decrease by number of loss events 21 int m_iAvgNAKNum; // average number of NAKs per congestion 22 int m_iDecCount; // number of decreases in a congestion epoch 23};
然后是UDT中默认的CCC CUDTCC的实现:
1CUDTCC::CUDTCC() 2 : m_iRCInterval(), 3 m_LastRCTime(), 4 m_bSlowStart(), 5 m_iLastAck(), 6 m_bLoss(), 7 m_iLastDecSeq(), 8 m_dLastDecPeriod(), 9 m_iNAKCount(), 10 m_iDecRandom(), 11 m_iAvgNAKNum(), 12 m_iDecCount() { 13} 14 15void CUDTCC::init() { 16 m_iRCInterval = m_iSYNInterval; 17 m_LastRCTime = CTimer::getTime(); 18 setACKTimer(m_iRCInterval); 19 20 m_bSlowStart = true; 21 m_iLastAck = m_iSndCurrSeqNo; 22 m_bLoss = false; 23 m_iLastDecSeq = CSeqNo::decseq(m_iLastAck); 24 m_dLastDecPeriod = 1; 25 m_iAvgNAKNum = 0; 26 m_iNAKCount = 0; 27 m_iDecRandom = 1; 28 29 m_dCWndSize = 16; 30 m_dPktSndPeriod = 1; 31} 32 33void CUDTCC::onACK(int32_t ack) { 34 int64_t B = 0; 35 double inc = 0; 36 // Note: 1/24/2012 37 // The minimum increase parameter is increased from "1.0 / m_iMSS" to 0.01 38 // because the original was too small and caused sending rate to stay at low level 39 // for long time. 40 const double min_inc = 0.01; 41 42 uint64_t currtime = CTimer::getTime(); 43 if (currtime - m_LastRCTime < (uint64_t) m_iRCInterval) 44 return; 45 46 m_LastRCTime = currtime; 47 48 if (m_bSlowStart) { 49 m_dCWndSize += CSeqNo::seqlen(m_iLastAck, ack); 50 m_iLastAck = ack; 51 52 if (m_dCWndSize > m_dMaxCWndSize) { 53 m_bSlowStart = false; 54 if (m_iRcvRate > 0) 55 m_dPktSndPeriod = 1000000.0 / m_iRcvRate; 56 else 57 m_dPktSndPeriod = (m_iRTT + m_iRCInterval) / m_dCWndSize; 58 } 59 } else 60 m_dCWndSize = m_iRcvRate / 1000000.0 * (m_iRTT + m_iRCInterval) + 16; 61 62 // During Slow Start, no rate increase 63 if (m_bSlowStart) 64 return; 65 66 if (m_bLoss) { 67 m_bLoss = false; 68 return; 69 } 70 71 B = (int64_t) (m_iBandwidth - 1000000.0 / m_dPktSndPeriod); 72 if ((m_dPktSndPeriod > m_dLastDecPeriod) && ((m_iBandwidth / 9) < B)) 73 B = m_iBandwidth / 9; 74 if (B <= 0) 75 inc = min_inc; 76 else { 77 // inc = max(10 ^ ceil(log10( B * MSS * 8 ) * Beta / MSS, 1/MSS) 78 // Beta = 1.5 * 10^(-6) 79 80 inc = pow(10.0, ceil(log10(B * m_iMSS * 8.0))) * 0.0000015 / m_iMSS; 81 82 if (inc < min_inc) 83 inc = min_inc; 84 } 85 86 m_dPktSndPeriod = (m_dPktSndPeriod * m_iRCInterval) / (m_dPktSndPeriod * inc + m_iRCInterval); 87} 88 89void CUDTCC::onLoss(const int32_t* losslist, int) { 90 //Slow Start stopped, if it hasn't yet 91 if (m_bSlowStart) { 92 m_bSlowStart = false; 93 if (m_iRcvRate > 0) { 94 // Set the sending rate to the receiving rate. 95 m_dPktSndPeriod = 1000000.0 / m_iRcvRate; 96 return; 97 } 98 // If no receiving rate is observed, we have to compute the sending 99 // rate according to the current window size, and decrease it 100 // using the method below. 101 m_dPktSndPeriod = m_dCWndSize / (m_iRTT + m_iRCInterval); 102 } 103 104 m_bLoss = true; 105 106 if (CSeqNo::seqcmp(losslist[0] & 0x7FFFFFFF, m_iLastDecSeq) > 0) { 107 m_dLastDecPeriod = m_dPktSndPeriod; 108 m_dPktSndPeriod = ceil(m_dPktSndPeriod * 1.125); 109 110 m_iAvgNAKNum = (int) ceil(m_iAvgNAKNum * 0.875 + m_iNAKCount * 0.125); 111 m_iNAKCount = 1; 112 m_iDecCount = 1; 113 114 m_iLastDecSeq = m_iSndCurrSeqNo; 115 116 // remove global synchronization using randomization 117 srand(m_iLastDecSeq); 118 m_iDecRandom = (int) ceil(m_iAvgNAKNum * (double(rand()) / RAND_MAX)); 119 if (m_iDecRandom < 1) 120 m_iDecRandom = 1; 121 } else if ((m_iDecCount++ < 5) && (0 == (++m_iNAKCount % m_iDecRandom))) { 122 // 0.875^5 = 0.51, rate should not be decreased by more than half within a congestion period 123 m_dPktSndPeriod = ceil(m_dPktSndPeriod * 1.125); 124 m_iLastDecSeq = m_iSndCurrSeqNo; 125 } 126} 127 128void CUDTCC::onTimeout() { 129 if (m_bSlowStart) { 130 m_bSlowStart = false; 131 if (m_iRcvRate > 0) 132 m_dPktSndPeriod = 1000000.0 / m_iRcvRate; 133 else 134 m_dPktSndPeriod = m_dCWndSize / (m_iRTT + m_iRCInterval); 135 } else { 136 /* 137 m_dLastDecPeriod = m_dPktSndPeriod; 138 m_dPktSndPeriod = ceil(m_dPktSndPeriod * 2); 139 m_iLastDecSeq = m_iLastAck; 140 */ 141 } 142}
具体的算法这里就不再仔细厘清了。
拥塞控制器就像是一个加工长一样,接收一些事件,经过自己的处理,输出一些数据来控制数据的收发过程。拥塞控制器输出的数据主要有:
1. 包发送周期m_dPktSndPeriod用于控制数据包的发送周期。
2. 拥塞控制窗口大小m_dCWndSize用于控制发送窗口的大小。
3. ACK发送周期m_iACKPeriod和ACK发送间隔m_iACKInterval用于控制ACK包发送的频率。
4. m_bUserDefinedRTO和m_iRTO用于控制超时时间。
如我们前面看到的m_dPktSndPeriod和m_dCWndSize会在CUDT::CCUpdate()中同步给CUDT。
Done。