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  }