363 lines
12 KiB
C#
363 lines
12 KiB
C#
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;
|
|
*/
|
|
}
|
|
}
|
|
}
|
|
}
|