mirror of
https://github.com/impact-eintr/netstack.git
synced 2025-10-05 12:56:55 +08:00
104 lines
3.4 KiB
Go
104 lines
3.4 KiB
Go
package tcp
|
||
|
||
import (
|
||
"log"
|
||
"netstack/logger"
|
||
)
|
||
|
||
type renoState struct {
|
||
s *sender
|
||
cnt int
|
||
}
|
||
|
||
// 新建reno算法对象
|
||
func newRenoCC(s *sender) *renoState {
|
||
return &renoState{s: s}
|
||
}
|
||
|
||
// updateSlowStart 将根据NewReno使用的慢启动算法更新拥塞窗口。
|
||
// 如果在调整拥塞窗口后我们越过了 SSthreshold ,那么它将返回在拥塞避免模式下必须消耗的数据包的数量。
|
||
func (r *renoState) updateSlowStart(packetsAcked int) int {
|
||
// 在慢启动阶段,每当收到一个 ACK,cwnd++; 呈线性上升
|
||
oldcwnd := r.s.sndCwnd
|
||
newcwnd := r.s.sndCwnd + packetsAcked
|
||
// 判断增大过后的拥塞窗口是否超过慢启动阀值 sndSsthresh,
|
||
// 如果超过 sndSsthresh ,将窗口调整为 sndSsthresh
|
||
if newcwnd >= r.s.sndSsthresh {
|
||
newcwnd = r.s.sndSsthresh
|
||
r.s.sndCAAckCount = 0
|
||
}
|
||
// 是否超过 sndSsthresh, packetsAcked>0表示超过 没超过就是0
|
||
packetsAcked -= newcwnd - r.s.sndCwnd
|
||
// 更新拥塞窗口
|
||
r.s.sndCwnd = newcwnd
|
||
r.cnt++
|
||
logger.GetInstance().Info(logger.TCP, func() {
|
||
logger.NOTICE("一个 RTT 已经结束", atoi(oldcwnd), "慢启动中。。。 reno Update 新的拥塞窗口大小: ", atoi(r.s.sndCwnd), "轮次", atoi(r.cnt))
|
||
})
|
||
return packetsAcked
|
||
}
|
||
|
||
// updateCongestionAvoidance 将在拥塞避免模式下更新拥塞窗口,
|
||
// 如RFC5681第3.1节所述
|
||
// 每当收到一个 ACK 时,cwnd = cwnd + 1/cwnd
|
||
// 每当过一个 RTT 时,cwnd = cwnd + 1
|
||
func (r *renoState) updateCongestionAvoidance(packetsAcked int) {
|
||
// sndCAAckCount 累计收到的tcp段数
|
||
r.s.sndCAAckCount += packetsAcked
|
||
// 如果累计的段数超过当前的拥塞窗口,那么 sndCwnd 加上 sndCAAckCount/sndCwnd 的整数倍
|
||
if r.s.sndCAAckCount >= r.s.sndCwnd {
|
||
r.s.sndCwnd += r.s.sndCAAckCount / r.s.sndCwnd
|
||
r.s.sndCAAckCount = r.s.sndCAAckCount % r.s.sndCwnd
|
||
}
|
||
}
|
||
|
||
// 当检测到网络拥塞时,调用 reduceSlowStartThreshold。
|
||
// 它将 sndSsthresh 变为 outstanding 的一半。
|
||
// sndSsthresh 最小为2,因为至少要比丢包后的拥塞窗口(cwnd=1)来的大,才会进入慢启动阶段。
|
||
func (r *renoState) reduceSlowStartThreshold() {
|
||
r.s.sndSsthresh = r.s.outstanding/2
|
||
if r.s.sndSsthresh < 2 {
|
||
r.s.sndSsthresh = 2
|
||
}
|
||
}
|
||
|
||
// HandleNDupAcks implements congestionControl.HandleNDupAcks.
|
||
// 当收到三个重复ack时,调用 HandleNDupAcks 来处理。
|
||
func (r *renoState) HandleNDupAcks() {
|
||
// A retransmit was triggered due to nDupAckThreshold
|
||
// being hit. Reduce our slow start threshold.
|
||
// 减小慢启动阀值
|
||
r.reduceSlowStartThreshold()
|
||
}
|
||
|
||
func (r *renoState) HandleRTOExpired() {
|
||
// We lost a packet, so reduce ssthresh.
|
||
// 减小慢启动阀值
|
||
r.reduceSlowStartThreshold()
|
||
|
||
// Reduce the congestion window to 1, i.e., enter slow-start. Per
|
||
// RFC 5681, page 7, we must use 1 regardless of the value of the
|
||
// initial congestion window.
|
||
// 更新拥塞窗口为1,这样就会重新进入慢启动
|
||
log.Fatal("重新进入慢启动")
|
||
r.s.sndCwnd = 1
|
||
}
|
||
|
||
// packetsAcked 已经确认过的数据段数
|
||
func (r *renoState) Update(packetsAcked int) {
|
||
// 当拥塞窗口没有超过慢启动阀值的时候,使用慢启动来增大窗口,
|
||
// 否则进入拥塞避免阶段
|
||
if r.s.sndCwnd < r.s.sndSsthresh {
|
||
packetsAcked = r.updateSlowStart(packetsAcked)
|
||
if packetsAcked == 0 {
|
||
return
|
||
}
|
||
}
|
||
// 当拥塞窗口大于阈值时 进入拥塞避免阶段
|
||
r.updateCongestionAvoidance(packetsAcked)
|
||
}
|
||
|
||
func (r *renoState) PostRecovery() {
|
||
// 不需要实现
|
||
}
|