using System; using UDTSOCKET = System.Int32; namespace UdtSharp { public class CC { protected const int m_iSYNInterval = UDT.m_iSYNInterval; // UDT constant parameter, SYN public double m_dPktSndPeriod; // Packet sending period, in microseconds public double m_dCWndSize; // Congestion window size, in packets protected int m_iBandwidth; // estimated bandwidth, packets per second protected double m_dMaxCWndSize; // maximum cwnd size, in packets protected int m_iMSS; // Maximum Packet Size, including all packet headers protected int m_iSndCurrSeqNo; // current maximum seq no sent out protected int m_iRcvRate; // packet arrive rate at receiver side, packets per second protected int m_iRTT; // current estimated RTT, microsecond protected string m_pcParam; // user defined parameter public UDTSOCKET m_UDT; // The UDT entity that this congestion control algorithm is bound to public int m_iACKPeriod; // Periodical timer to send an ACK, in milliseconds public int m_iACKInterval; // How many packets to send one ACK, in packets public bool m_bUserDefinedRTO; // if the RTO value is defined by users public int m_iRTO; // RTO value, microseconds PerfMon m_PerfInfo = new PerfMon(); // protocol statistics information public CC() { m_dPktSndPeriod = 1.0; m_dCWndSize = 16.0; m_pcParam = null; m_iACKPeriod = 0; m_iACKInterval = 0; m_bUserDefinedRTO = false; m_iRTO = -1; } // Functionality: // Callback function to be called (only) at the start of a UDT connection. // note that this is different from CCC(), which is always called. // Parameters: // None. // Returned value: // None. public virtual void init() { } // Functionality: // Callback function to be called when a UDT connection is closed. // Parameters: // None. // Returned value: // None. public virtual void close() { } // Functionality: // Callback function to be called when an ACK packet is received. // Parameters: // 0) [in] ackno: the data sequence number acknowledged by this ACK. // Returned value: // None. public virtual void onACK(int seqno) { } // Functionality: // Callback function to be called when a loss report is received. // Parameters: // 0) [in] losslist: list of sequence number of packets, in the format describled in packet.cpp. // 1) [in] size: length of the loss list. // Returned value: // None. public virtual void onLoss(int[] loss, int length) { } // Functionality: // Callback function to be called when a timeout event occurs. // Parameters: // None. // Returned value: // None. public virtual void onTimeout() { } // Functionality: // Callback function to be called when a data is sent. // Parameters: // 0) [in] seqno: the data sequence number. // 1) [in] size: the payload size. // Returned value: // None. public virtual void onPktSent(Packet packet) { } // Functionality: // Callback function to be called when a data is received. // Parameters: // 0) [in] seqno: the data sequence number. // 1) [in] size: the payload size. // Returned value: // None. public virtual void onPktReceived(Packet packet) { } // Functionality: // Callback function to Process a user defined packet. // Parameters: // 0) [in] pkt: the user defined packet. // Returned value: // None. public virtual void processCustomMsg(Packet packet) { } protected void setACKTimer(int msINT) { m_iACKPeriod = msINT > m_iSYNInterval ? m_iSYNInterval : msINT; } protected void setACKInterval(int pktINT) { m_iACKInterval = pktINT; } protected void setRTO(int usRTO) { m_bUserDefinedRTO = true; m_iRTO = usRTO; } protected void sendCustomMsg(Packet pkt) { UDT u = UDT.s_UDTUnited.lookup(m_UDT); if (null != u) { pkt.SetId(u.m_PeerID); u.m_pSndQueue.sendto(u.m_pPeerAddr, pkt); } } protected PerfMon getPerfInfo() { try { UDT u = UDT.s_UDTUnited.lookup(m_UDT); if (null != u) u.sample(m_PerfInfo, false); } catch (Exception e) { return null; } return m_PerfInfo; } public void setMSS(int mss) { m_iMSS = mss; } public void setBandwidth(int bw) { m_iBandwidth = bw; } public void setSndCurrSeqNo(int seqno) { m_iSndCurrSeqNo = seqno; } public void setRcvRate(int rcvrate) { m_iRcvRate = rcvrate; } public void setMaxCWndSize(int cwnd) { m_dMaxCWndSize = cwnd; } public void setRTT(int rtt) { m_iRTT = rtt; } protected void setUserParam(string param) { m_pcParam = param; } } public class UDTCC : CC { int m_iRCInterval; // UDT Rate control interval ulong m_LastRCTime; // last rate increase time bool m_bSlowStart; // if in slow start phase int m_iLastAck; // last ACKed seq no bool m_bLoss; // if loss happened since last rate increase int m_iLastDecSeq; // max pkt seq no sent out when last decrease happened double m_dLastDecPeriod; // value of pktsndperiod when last decrease happened int m_iNAKCount; // NAK counter int m_iDecRandom; // random threshold on decrease by number of loss events int m_iAvgNAKNum; // average number of NAKs per congestion int m_iDecCount; // number of decreases in a congestion epoch static Random m_random = new Random(); public override void init() { m_iRCInterval = m_iSYNInterval; m_LastRCTime = Timer.getTime(); setACKTimer(m_iRCInterval); m_bSlowStart = true; m_iLastAck = m_iSndCurrSeqNo; m_bLoss = false; m_iLastDecSeq = SequenceNumber.decseq(m_iLastAck); m_dLastDecPeriod = 1; m_iAvgNAKNum = 0; m_iNAKCount = 0; m_iDecRandom = 1; m_dCWndSize = 16; m_dPktSndPeriod = 1; } public override void onACK(int ack) { long B = 0; double inc = 0; // Note: 1/24/2012 // The minimum increase parameter is increased from "1.0 / m_iMSS" to 0.01 // because the original was too small and caused sending rate to stay at low level // for long time. const double min_inc = 0.01; ulong currtime = Timer.getTime(); if (currtime - m_LastRCTime < (ulong)m_iRCInterval) return; m_LastRCTime = currtime; if (m_bSlowStart) { m_dCWndSize += SequenceNumber.seqlen(m_iLastAck, ack); m_iLastAck = ack; if (m_dCWndSize > m_dMaxCWndSize) { m_bSlowStart = false; if (m_iRcvRate > 0) m_dPktSndPeriod = 1000000.0 / m_iRcvRate; else m_dPktSndPeriod = (m_iRTT + m_iRCInterval) / m_dCWndSize; } } 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) { m_bLoss = false; return; } B = (long)(m_iBandwidth - 1000000.0 / m_dPktSndPeriod); if ((m_dPktSndPeriod > m_dLastDecPeriod) && ((m_iBandwidth / 9) < B)) B = m_iBandwidth / 9; if (B <= 0) inc = min_inc; else { // inc = max(10 ^ ceil(log10( B * MSS * 8 ) * Beta / MSS, 1/MSS) // Beta = 1.5 * 10^(-6) inc = Math.Pow(10.0, Math.Ceiling(Math.Log10(B * m_iMSS * 8.0))) * 0.0000015 / m_iMSS; if (inc < min_inc) inc = min_inc; } m_dPktSndPeriod = (m_dPktSndPeriod * m_iRCInterval) / (m_dPktSndPeriod * inc + m_iRCInterval); } public override void onLoss(int[] losslist, int length) { //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); } m_bLoss = true; if (SequenceNumber.seqcmp(losslist[0] & 0x7FFFFFFF, m_iLastDecSeq) > 0) { m_dLastDecPeriod = m_dPktSndPeriod; m_dPktSndPeriod = Math.Ceiling(m_dPktSndPeriod * 1.125); m_iAvgNAKNum = (int)Math.Ceiling(m_iAvgNAKNum * 0.875 + m_iNAKCount * 0.125); m_iNAKCount = 1; m_iDecCount = 1; m_iLastDecSeq = m_iSndCurrSeqNo; // remove global synchronization using randomization m_iDecRandom = (int)Math.Ceiling(m_iAvgNAKNum * m_random.NextDouble()); 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 = Math.Ceiling(m_dPktSndPeriod * 1.125); m_iLastDecSeq = m_iSndCurrSeqNo; } } public override void onTimeout() { if (m_bSlowStart) { m_bSlowStart = false; if (m_iRcvRate > 0) m_dPktSndPeriod = 1000000.0 / m_iRcvRate; else m_dPktSndPeriod = m_dCWndSize / (m_iRTT + m_iRCInterval); } else { /* m_dLastDecPeriod = m_dPktSndPeriod; m_dPktSndPeriod = ceil(m_dPktSndPeriod * 2); m_iLastDecSeq = m_iLastAck; */ } } } }