mirror of
https://github.com/PeernetOfficial/core.git
synced 2026-07-17 02:47:51 +01:00
229 lines
7.8 KiB
Go
229 lines
7.8 KiB
Go
package udt
|
|
|
|
import (
|
|
"math"
|
|
"math/rand"
|
|
"time"
|
|
|
|
"github.com/PeernetOfficial/core/udt/packet"
|
|
)
|
|
|
|
// NativeCongestionControl implements the default congestion control logic for UDP
|
|
type NativeCongestionControl struct {
|
|
rcInterval time.Duration // UDT Rate control interval
|
|
lastRCTime time.Time // last rate increase time
|
|
slowStart bool // if in slow start phase
|
|
lastAck packet.PacketID // last ACKed seq no
|
|
loss bool // if loss happened since last rate increase
|
|
lastDecSeq packet.PacketID // biggest sequence number when last time the packet sending rate is decreased
|
|
lastDecPeriod time.Duration // value of PacketSendPeriod when last decrease happened
|
|
nakCount int // current number of NAKs in the current period
|
|
decRandom int // random threshold on decrease by number of loss events
|
|
avgNAKNum int // average number of NAKs in a congestion period
|
|
decCount int // number of decreases in a congestion epoch
|
|
}
|
|
|
|
// Init to be called (only) at the start of a UDT connection.
|
|
func (ncc NativeCongestionControl) Init(parms CongestionControlParms, synTime time.Duration) {
|
|
ncc.rcInterval = synTime
|
|
ncc.lastRCTime = time.Now()
|
|
parms.SetACKPeriod(ncc.rcInterval)
|
|
|
|
ncc.slowStart = true
|
|
ncc.lastAck = parms.GetSndCurrSeqNo()
|
|
ncc.loss = false
|
|
ncc.lastDecSeq = ncc.lastAck.Add(-1)
|
|
ncc.lastDecPeriod = 1 * time.Microsecond
|
|
ncc.avgNAKNum = 0
|
|
ncc.nakCount = 0
|
|
ncc.decRandom = 1
|
|
|
|
parms.SetCongestionWindowSize(16)
|
|
parms.SetPacketSendPeriod(1 * time.Microsecond)
|
|
}
|
|
|
|
// Close to be called when a UDT connection is closed.
|
|
func (ncc NativeCongestionControl) Close(parms CongestionControlParms) {
|
|
// nothing done for this event
|
|
}
|
|
|
|
// OnACK to be called when an ACK packet is received
|
|
func (ncc NativeCongestionControl) OnACK(parms CongestionControlParms, ack packet.PacketID) {
|
|
currTime := time.Now()
|
|
if currTime.Sub(ncc.lastRCTime) < ncc.rcInterval {
|
|
return
|
|
}
|
|
ncc.lastRCTime = currTime
|
|
cWndSize := parms.GetCongestionWindowSize()
|
|
pktSendPeriod := parms.GetPacketSendPeriod()
|
|
recvRate, bandwidth := parms.GetReceiveRates()
|
|
rtt := parms.GetRTT()
|
|
|
|
// 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.
|
|
if ncc.slowStart {
|
|
cWndSize = uint(int(cWndSize) + int(ack.BlindDiff(ncc.lastAck)))
|
|
ncc.lastAck = ack
|
|
|
|
if cWndSize > parms.GetMaxFlowWindow() {
|
|
ncc.slowStart = false
|
|
if recvRate > 0 {
|
|
parms.SetPacketSendPeriod(time.Second / time.Duration(recvRate))
|
|
} else {
|
|
parms.SetPacketSendPeriod((rtt + ncc.rcInterval) / time.Duration(cWndSize))
|
|
}
|
|
} else {
|
|
// During Slow Start, no rate increase
|
|
parms.SetCongestionWindowSize(cWndSize)
|
|
return
|
|
}
|
|
} else {
|
|
// Set the congestion window size (CWND) to: CWND = A * (RTT + SYN) + 16.
|
|
cWndSize = uint((float64(recvRate)/float64(time.Second))*float64(rtt+ncc.rcInterval) + 16)
|
|
}
|
|
if ncc.loss {
|
|
ncc.loss = false
|
|
parms.SetCongestionWindowSize(cWndSize)
|
|
return
|
|
}
|
|
/*
|
|
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.
|
|
*/
|
|
|
|
// 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.
|
|
var inc float64
|
|
const minInc float64 = 0.01
|
|
|
|
B := time.Duration(bandwidth) - time.Second/time.Duration(pktSendPeriod)
|
|
bandwidth9 := time.Duration(bandwidth / 9)
|
|
if (pktSendPeriod > ncc.lastDecPeriod) && (bandwidth9 < B) {
|
|
B = bandwidth9
|
|
}
|
|
if B <= 0 {
|
|
inc = minInc
|
|
} else {
|
|
// inc = max(10 ^ ceil(log10( B * MSS * 8 ) * Beta / MSS, 1/MSS)
|
|
// Beta = 1.5 * 10^(-6)
|
|
|
|
mss := parms.GetMSS()
|
|
inc = math.Pow10(int(math.Ceil(math.Log10(float64(B)*float64(mss)*8.0)))) * 0.0000015 / float64(mss)
|
|
|
|
if inc < minInc {
|
|
inc = minInc
|
|
}
|
|
}
|
|
|
|
// The SND period is updated as: SND = (SND * SYN) / (SND * inc + SYN).
|
|
parms.SetPacketSendPeriod(time.Duration(float64(pktSendPeriod*ncc.rcInterval) / (float64(pktSendPeriod)*inc + float64(ncc.rcInterval))))
|
|
}
|
|
|
|
// OnNAK to be called when a loss report is received
|
|
func (ncc NativeCongestionControl) OnNAK(parms CongestionControlParms, losslist []packet.PacketID) {
|
|
// If it is in slow start phase, set inter-packet interval to 1/recvrate. Slow start ends. Stop.
|
|
if ncc.slowStart {
|
|
ncc.slowStart = false
|
|
recvRate, _ := parms.GetReceiveRates()
|
|
if recvRate > 0 {
|
|
// Set the sending rate to the receiving rate.
|
|
parms.SetPacketSendPeriod(time.Second / time.Duration(recvRate))
|
|
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.
|
|
parms.SetPacketSendPeriod(time.Duration(float64(time.Microsecond) * float64(parms.GetCongestionWindowSize()) / float64(parms.GetRTT()+ncc.rcInterval)))
|
|
}
|
|
|
|
ncc.loss = true
|
|
|
|
/*
|
|
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).
|
|
*/
|
|
pktSendPeriod := parms.GetPacketSendPeriod()
|
|
if ncc.lastDecSeq.BlindDiff(losslist[0]) > 0 {
|
|
ncc.lastDecPeriod = pktSendPeriod
|
|
parms.SetPacketSendPeriod(pktSendPeriod * 1125 / 1000)
|
|
|
|
ncc.avgNAKNum = int(math.Ceil(float64(ncc.avgNAKNum)*0.875 + float64(ncc.nakCount)*0.125))
|
|
ncc.nakCount = 1
|
|
ncc.decCount = 1
|
|
|
|
ncc.lastDecSeq = parms.GetSndCurrSeqNo()
|
|
|
|
// remove global synchronization using randomization
|
|
rand := float64(rand.Uint32()) / math.MaxUint32
|
|
ncc.decRandom = int(math.Ceil(float64(ncc.avgNAKNum) * rand))
|
|
if ncc.decRandom < 1 {
|
|
ncc.decRandom = 1
|
|
}
|
|
} else {
|
|
if ncc.decCount < 5 {
|
|
ncc.nakCount++
|
|
if ncc.nakCount%ncc.decRandom != 0 {
|
|
ncc.decCount++
|
|
return
|
|
}
|
|
}
|
|
ncc.decCount++
|
|
|
|
// 0.875^5 = 0.51, rate should not be decreased by more than half within a congestion period
|
|
parms.SetPacketSendPeriod(pktSendPeriod * 1125 / 1000)
|
|
ncc.lastDecSeq = parms.GetSndCurrSeqNo()
|
|
}
|
|
}
|
|
|
|
// OnTimeout to be called when a timeout event occurs
|
|
func (ncc NativeCongestionControl) OnTimeout(parms CongestionControlParms) {
|
|
if ncc.slowStart {
|
|
ncc.slowStart = false
|
|
recvRate, _ := parms.GetReceiveRates()
|
|
if recvRate > 0 {
|
|
parms.SetPacketSendPeriod(time.Second / time.Duration(recvRate))
|
|
} else {
|
|
parms.SetPacketSendPeriod(time.Duration(float64(time.Microsecond) * float64(parms.GetCongestionWindowSize()) / float64(parms.GetRTT()+ncc.rcInterval)))
|
|
}
|
|
} else {
|
|
/*
|
|
pktSendPeriod := parms.GetPacketSendPeriod()
|
|
ncc.lastDecPeriod = pktSendPeriod
|
|
parms.SetPacketSendPeriod(math.Ceil(pktSendPeriod * 2))
|
|
ncc.lastDecSeq = ncc.lastAck
|
|
*/
|
|
}
|
|
}
|
|
|
|
// OnPktSent to be called when data is sent
|
|
func (ncc NativeCongestionControl) OnPktSent(parms CongestionControlParms, pkt packet.Packet) {
|
|
// nothing done for this event
|
|
}
|
|
|
|
// OnPktRecv to be called when a data is received
|
|
func (ncc NativeCongestionControl) OnPktRecv(parms CongestionControlParms, pkt packet.DataPacket) {
|
|
// nothing done for this event
|
|
}
|
|
|
|
// OnCustomMsg to process a user-defined packet
|
|
func (ncc NativeCongestionControl) OnCustomMsg(parms CongestionControlParms, pkt packet.UserDefControlPacket) {
|
|
// nothing done for this event
|
|
}
|