快速重传失效了

This commit is contained in:
impact-eintr
2022-12-26 18:00:37 +08:00
parent 94e12c1f21
commit 823e76e979
5 changed files with 63 additions and 9 deletions

View File

@@ -184,7 +184,7 @@ func main() {
log.Printf("\n\n客户端 写入数据")
cnt := 0
for i := 0; i < 10; i++ {
for i := 0; i < 20; i++ {
conn.Write(make([]byte, 1<<(5)))
cnt += 1<<(5)
//buf := make([]byte, 1024)

View File

@@ -2,12 +2,10 @@ package loopback
import (
"fmt"
"math/rand"
"netstack/logger"
"netstack/tcpip"
"netstack/tcpip/buffer"
"netstack/tcpip/stack"
"time"
)
type endpoint struct {
@@ -49,11 +47,11 @@ func (e *endpoint) WritePacket(r *stack.Route, hdr buffer.Prependable, payload b
vv := buffer.NewVectorisedView(len(views[0])+payload.Size(), views)
// TODO 这里整点活 在特定的情况下丢掉数据报 模拟网络阻塞
rand.Seed(time.Now().Unix())
//rand.Seed(time.Now().Unix())
//time.Sleep(time.Duration(rand.Intn(50)+50) * time.Millisecond)
e.count++
if e.count == -1 { // 丢掉客户端写入的第二个包
if e.count == 10 { // 丢掉客户端写入的第二个包
logger.NOTICE(fmt.Sprintf("统计 %d 丢掉这个报文", e.count))
return nil
}

View File

@@ -324,8 +324,27 @@ TCP 通过维护一个拥塞窗口(cwnd 全称 Congestion Window)来进行拥塞
### 慢启动slow start
慢启动的意思是,加入网络的连接,一点一点地提速,不要一上来就突发流量挤占通道。 慢启动的算法如下:
1. 连接建好的开始先初始化cwnd = 1表明可以传一个 MSS 大小的数据。
2. 每当收到一个 ACKcwnd++; 呈线性上升
3. 每当过了一个 RTTcwnd = cwnd*2; 呈指数上升
4. 设置一个慢启动阀值 ssthreshslow start threshold是慢启动和拥塞避免的一个临界值当cwnd >= ssthresh时就会进入拥塞避免阶段
### 拥塞避免congestion avoidance
当 cwnd >= ssthresh 时,就会进入“拥塞避免算法”。一般来说初始 ssthresh 的值是很大的,当 cwnd 达到这个值时后,算法如下:
- 每当收到一个 ACK 时cwnd = cwnd + 1/cwnd
- 每当过一个 RTT 时cwnd = cwnd + 1
### 快速重传Fast Retransmit
### 快速恢复Fast Recovery
- 1988 年TCP Tahoe 提出了 1慢启动2拥塞避免3快速重传。
- 1990 年TCP Reno 在 Tahoe 的基础上增加了 4快速恢复是现有的众多拥塞控制算法的基础被认为标准 tcp 拥塞行为。
- 2004 年TCP BICBinary Increase Congestion control在Linux 2.6.8中是默认拥塞控制算法,用的是 Binary Search——二分查找来找拥塞窗口。
- 2008 年TCP CUBIC 是比 BIC 更温和和系统化的分支版本,其使用三次函数代替二分算法作为其拥塞窗口算法,并且使用函数拐点作为拥塞窗口的设置值。 Linux 2.6.19后使用该算法作为默认 TCP 拥塞算法,现在也是。
- 2016 年TCP BBR 是由 Google 设计,于 2016 年发布的拥塞算法,交替测量一段时间内的带宽极大值和时延极小值,将其乘积作为作为拥塞窗口大小,认为当网络上的数据包总量大于瓶颈链路带宽和时延的乘积时才出现了拥塞。

View File

@@ -1,6 +1,8 @@
package tcp
import "netstack/logger"
import (
"netstack/logger"
)
type renoState struct {
s *sender

View File

@@ -506,8 +506,8 @@ func (s *sender) retransmitTimerExpired() bool {
s.outstanding = 0
s.writeNext = s.writeList.Front()
// 重新发送数据包
logger.NOTICE("超时重发")
s.sendData()
logger.NOTICE("暂时关闭超时重发", s.rto.String())
//s.sendData()
return true
}
@@ -634,6 +634,7 @@ func (s *sender) leaveFastRecovery() {
// Deflate cwnd. It had been artificially inflated when new dups arrived.
s.sndCwnd = s.sndSsthresh
s.cc.PostRecovery()
logger.NOTICE("退出快速恢复")
}
@@ -641,9 +642,43 @@ func (s *sender) leaveFastRecovery() {
// 并根据RFC 6582NewReno中的规则确定是否需要重新传输
func (s *sender) checkDuplicateAck(seg *segment) (rtx bool) {
ack := seg.ackNumber
//logger.NOTICE("注意测试", atoi(s.sndCwnd))
// 已经启动了快速恢复
if s.fr.active {
log.Fatal("启动了快速恢复")
if !ack.InRange(s.sndUna, s.sndNxt+1) {
return false
}
// 如果它确认此快速恢复涵盖的所有数据,退出快速恢复。
if s.fr.last.LessThan(ack) {
s.leaveFastRecovery()
return false
}
// 如果该tcp段有负载或者正在更新窗口那么不计算这个ack
if seg.logicalLen() != 0 || s.sndWnd != seg.window {
return false
}
// Inflate the congestion window if we're getting duplicate acks
// for the packet we retransmitted.
if ack == s.fr.first {
// We received a dup, inflate the congestion window by 1
// packet if we're not at the max yet.
if s.sndCwnd < s.fr.maxCwnd {
s.sndCwnd++
}
return false
}
// A partial ack was received. Retransmit this packet and
// remember it so that we don't retransmit it again. We don't
// inflate the window because we're putting the same packet back
// onto the wire.
//
// N.B. The retransmit timer will be reset by the caller.
s.fr.first = ack
s.dupAckCount = 0
return true
}
// 我们还没有进入快速恢复状态,只有当段不携带任何数据并且不更新发送窗口时,才认为该段是重复的。