|
|
@@ -0,0 +1,155 @@
|
|
|
+package kcp
|
|
|
+
|
|
|
+import (
|
|
|
+ "sync"
|
|
|
+)
|
|
|
+
|
|
|
+const (
|
|
|
+ defaultRTT = 100
|
|
|
+ queueSize = 10
|
|
|
+)
|
|
|
+
|
|
|
+type Queue struct {
|
|
|
+ value [queueSize]uint32
|
|
|
+ start uint32
|
|
|
+ length uint32
|
|
|
+}
|
|
|
+
|
|
|
+func (v *Queue) Push(value uint32) {
|
|
|
+ if v.length < queueSize {
|
|
|
+ v.value[v.length] = value
|
|
|
+ v.length++
|
|
|
+ return
|
|
|
+ }
|
|
|
+ v.value[v.start] = value
|
|
|
+ v.start++
|
|
|
+ if v.start == queueSize {
|
|
|
+ v.start = 0
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func (v *Queue) Max() uint32 {
|
|
|
+ max := v.value[0]
|
|
|
+ for i := 1; i < queueSize; i++ {
|
|
|
+ if v.value[i] > max {
|
|
|
+ max = v.value[i]
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return max
|
|
|
+}
|
|
|
+
|
|
|
+func (v *Queue) Min() uint32 {
|
|
|
+ max := v.value[0]
|
|
|
+ for i := 1; i < queueSize; i++ {
|
|
|
+ if v.value[i] < max {
|
|
|
+ max = v.value[i]
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return max
|
|
|
+}
|
|
|
+
|
|
|
+type CongestionState byte
|
|
|
+
|
|
|
+const (
|
|
|
+ CongestionStateRTTProbe CongestionState = iota
|
|
|
+ CongestionStateBandwidthProbe
|
|
|
+ CongestionStateTransfer
|
|
|
+)
|
|
|
+
|
|
|
+type Congestion struct {
|
|
|
+ sync.RWMutex
|
|
|
+
|
|
|
+ state CongestionState
|
|
|
+ stateSince uint32
|
|
|
+ limit uint32 // bytes per 1000 seconds
|
|
|
+
|
|
|
+ rtt uint32 // millisec
|
|
|
+ rttHistory Queue
|
|
|
+ rttUpdateTime uint32
|
|
|
+
|
|
|
+ initialThroughput uint32 // bytes per 1000 seconds
|
|
|
+
|
|
|
+ cycleStartTime uint32
|
|
|
+ cycleBytesConfirmed uint32
|
|
|
+ cycleBytesSent uint32
|
|
|
+ cycleBytesLimit uint32
|
|
|
+
|
|
|
+ cycle uint32
|
|
|
+ bestCycleBytesConfirmed uint32
|
|
|
+ bestCycleBytesSent uint32
|
|
|
+}
|
|
|
+
|
|
|
+func (v *Congestion) SetState(current uint32, state CongestionState) {
|
|
|
+ v.state = state
|
|
|
+ v.stateSince = current
|
|
|
+}
|
|
|
+
|
|
|
+func (v *Congestion) Update(current uint32) {
|
|
|
+ switch v.state {
|
|
|
+ case CongestionStateRTTProbe:
|
|
|
+ if v.rtt > 0 {
|
|
|
+ v.SetState(current, CongestionStateBandwidthProbe)
|
|
|
+ v.cycleStartTime = current
|
|
|
+ v.cycleBytesConfirmed = 0
|
|
|
+ v.cycleBytesSent = 0
|
|
|
+ v.cycleBytesLimit = v.initialThroughput * v.rtt / 1000 / 1000
|
|
|
+ }
|
|
|
+ case CongestionStateBandwidthProbe:
|
|
|
+ if current-v.cycleStartTime >= v.rtt {
|
|
|
+
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func (v *Congestion) AddBytesConfirmed(current uint32, bytesConfirmed uint32) {
|
|
|
+ v.Lock()
|
|
|
+ defer v.Unlock()
|
|
|
+
|
|
|
+ v.cycleBytesConfirmed += bytesConfirmed
|
|
|
+ v.Update(current)
|
|
|
+}
|
|
|
+
|
|
|
+func (v *Congestion) UpdateRTT(current uint32, rtt uint32) {
|
|
|
+ v.Lock()
|
|
|
+ defer v.Unlock()
|
|
|
+
|
|
|
+ if v.state == CongestionStateRTTProbe || rtt < v.rtt {
|
|
|
+ v.rtt = rtt
|
|
|
+ v.rttUpdateTime = current
|
|
|
+ }
|
|
|
+
|
|
|
+ v.Update(current)
|
|
|
+}
|
|
|
+
|
|
|
+func (v *Congestion) GetBytesLimit() uint32 {
|
|
|
+ v.RLock()
|
|
|
+ defer v.RUnlock()
|
|
|
+
|
|
|
+ if v.state == CongestionStateRTTProbe {
|
|
|
+ return v.initialThroughput/1000/(1000/defaultRTT) - v.cycleBytesSent
|
|
|
+ }
|
|
|
+
|
|
|
+ return v.cycleBytesLimit
|
|
|
+}
|
|
|
+
|
|
|
+func (v *Congestion) RoundTripTime() uint32 {
|
|
|
+ v.RLock()
|
|
|
+ defer v.RUnlock()
|
|
|
+
|
|
|
+ if v.state == CongestionStateRTTProbe {
|
|
|
+ return defaultRTT
|
|
|
+ }
|
|
|
+
|
|
|
+ return v.rtt
|
|
|
+}
|
|
|
+
|
|
|
+func (v *Congestion) Timeout() uint32 {
|
|
|
+ v.RLock()
|
|
|
+ defer v.RUnlock()
|
|
|
+
|
|
|
+ if v.state == CongestionStateRTTProbe {
|
|
|
+ return defaultRTT * 3 / 2
|
|
|
+ }
|
|
|
+
|
|
|
+ return v.rtt * 3 / 2
|
|
|
+}
|