github.com/geph-official/geph2@v0.22.6-0.20210211030601-f527cb59b0df/libs/kcppp/session.go (about) 1 package kcppp 2 3 import ( 4 "log" 5 "os" 6 "sync" 7 ) 8 9 var doLogging = false 10 11 const mss = 1300 12 13 func init() { 14 doLogging = os.Getenv("KCPLOG") != "" 15 } 16 17 // session is a KCP++ session. 18 type session struct { 19 convID uint32 20 inFlight []annSegment 21 nextFreeSN uint32 22 remAckSN uint32 23 locAckSN uint32 24 25 rtoTimer *resettableTimer 26 27 isDead bool 28 29 lock sync.Mutex 30 cvar *sync.Cond 31 32 toSend []inlineBytes 33 toRecv []inlineBytes 34 sendCallback func(seg segment) 35 } 36 37 func newSession(convID uint32, cback func(seg segment)) *session { 38 sess := &session{ 39 convID: convID, 40 rtoTimer: newTimer(), 41 42 sendCallback: cback, 43 } 44 sess.cvar = sync.NewCond(&sess.lock) 45 go sess.writeLoop() 46 return sess 47 } 48 49 // annSegment is an "annotated" segment with state info. 50 type annSegment struct { 51 seg segment 52 acked bool 53 retransTimes int 54 } 55 56 // writeLoop is the main writing loop of the session. 57 func (sess *session) writeLoop() { 58 sess.lock.Lock() 59 defer sess.lock.Unlock() 60 defer func() { 61 sess.isDead = true 62 sess.cvar.Broadcast() 63 }() 64 // maximum send window size 65 maxInflight := 1000 66 for !sess.isDead { 67 // first, wait for space in window 68 for len(sess.inFlight) >= maxInflight { 69 sess.cvar.Wait() 70 if sess.isDead { 71 return 72 } 73 } 74 // send as much stuff as possible 75 for len(sess.inFlight) < maxInflight && len(sess.toSend) > 0 { 76 var seg segment 77 seg.Body = sess.toSend[0] 78 sess.toSend = sess.toSend[1:] 79 seg.Header = segHeader{ 80 ConvID: sess.convID, 81 Cmd: cmdPUSH, 82 Window: 1000, 83 Timestamp: currentMS(), 84 Seqno: sess.nextFreeSN, 85 Ackno: sess.locAckSN, 86 } 87 sess.nextFreeSN++ 88 sess.inFlight = append(sess.inFlight, annSegment{ 89 seg: seg, 90 }) 91 sess.sendCallback(seg) 92 sess.setTimer() 93 if doLogging { 94 log.Println(sess.convID, "sending seqno", seg.Header.Seqno) 95 } 96 } 97 } 98 } 99 100 // segInput inputs a packet into the state machine 101 func (sess *session) segInput(seg segment) { 102 // check convo id 103 if seg.Header.ConvID != sess.convID { 104 if doLogging { 105 log.Println(sess.convID, "rejecting bad ConvID") 106 } 107 return 108 } 109 sess.lock.Lock() 110 defer sess.lock.Unlock() 111 if seg.Header.Cmd == cmdRST { 112 113 } 114 switch seg.Header.Cmd { 115 case cmdRST: 116 sess.isDead = true 117 return 118 case cmdACK: 119 } 120 // update UNA 121 if seg.Header.Ackno > sess.remAckSN { 122 sess.remAckSN = seg.Header.Ackno 123 } 124 // mark as acked 125 for _, seg := range sess.inFlight { 126 if diff32(sess.remAckSN, seg.seg.Header.Seqno) > 0 { 127 seg.acked = true 128 } 129 } 130 // trim acked 131 for len(sess.inFlight) > 0 && sess.inFlight[0].acked { 132 sess.inFlight = sess.inFlight[1:] 133 } 134 }