mtu_discoverer.go 2.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384
  1. package quic
  2. import (
  3. "time"
  4. "github.com/Psiphon-Labs/quic-go/internal/ackhandler"
  5. "github.com/Psiphon-Labs/quic-go/internal/protocol"
  6. "github.com/Psiphon-Labs/quic-go/internal/utils"
  7. "github.com/Psiphon-Labs/quic-go/internal/wire"
  8. )
  9. type mtuDiscoverer interface {
  10. ShouldSendProbe(now time.Time) bool
  11. NextProbeTime() time.Time
  12. GetPing() (ping ackhandler.Frame, datagramSize protocol.ByteCount)
  13. }
  14. const (
  15. // At some point, we have to stop searching for a higher MTU.
  16. // We're happy to send a packet that's 10 bytes smaller than the actual MTU.
  17. maxMTUDiff = 20
  18. // send a probe packet every mtuProbeDelay RTTs
  19. mtuProbeDelay = 5
  20. )
  21. type mtuFinder struct {
  22. lastProbeTime time.Time
  23. probeInFlight bool
  24. mtuIncreased func(protocol.ByteCount)
  25. rttStats *utils.RTTStats
  26. current protocol.ByteCount
  27. max protocol.ByteCount // the maximum value, as advertised by the peer (or our maximum size buffer)
  28. }
  29. var _ mtuDiscoverer = &mtuFinder{}
  30. func newMTUDiscoverer(rttStats *utils.RTTStats, start, max protocol.ByteCount, mtuIncreased func(protocol.ByteCount)) mtuDiscoverer {
  31. return &mtuFinder{
  32. current: start,
  33. rttStats: rttStats,
  34. lastProbeTime: time.Now(), // to make sure the first probe packet is not sent immediately
  35. mtuIncreased: mtuIncreased,
  36. max: max,
  37. }
  38. }
  39. func (f *mtuFinder) done() bool {
  40. return f.max-f.current <= maxMTUDiff+1
  41. }
  42. func (f *mtuFinder) ShouldSendProbe(now time.Time) bool {
  43. if f.probeInFlight || f.done() {
  44. return false
  45. }
  46. return !now.Before(f.NextProbeTime())
  47. }
  48. // NextProbeTime returns the time when the next probe packet should be sent.
  49. // It returns the zero value if no probe packet should be sent.
  50. func (f *mtuFinder) NextProbeTime() time.Time {
  51. if f.probeInFlight || f.done() {
  52. return time.Time{}
  53. }
  54. return f.lastProbeTime.Add(mtuProbeDelay * f.rttStats.SmoothedRTT())
  55. }
  56. func (f *mtuFinder) GetPing() (ackhandler.Frame, protocol.ByteCount) {
  57. size := (f.max + f.current) / 2
  58. f.lastProbeTime = time.Now()
  59. f.probeInFlight = true
  60. return ackhandler.Frame{
  61. Frame: &wire.PingFrame{},
  62. OnLost: func(wire.Frame) {
  63. f.probeInFlight = false
  64. f.max = size
  65. },
  66. OnAcked: func(wire.Frame) {
  67. f.probeInFlight = false
  68. f.current = size
  69. f.mtuIncreased(size)
  70. },
  71. }, size
  72. }