|  | @@ -6,9 +6,9 @@
 | 
	
		
			
				|  |  |  package kcp
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  import (
 | 
	
		
			
				|  |  | -	"encoding/binary"
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |  	"github.com/v2ray/v2ray-core/common/alloc"
 | 
	
		
			
				|  |  | +	v2io "github.com/v2ray/v2ray-core/common/io"
 | 
	
		
			
				|  |  | +	"github.com/v2ray/v2ray-core/common/log"
 | 
	
		
			
				|  |  |  )
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  const (
 | 
	
	
		
			
				|  | @@ -31,45 +31,6 @@ const (
 | 
	
		
			
				|  |  |  	IKCP_PROBE_LIMIT = 120000 // up to 120 secs to probe window
 | 
	
		
			
				|  |  |  )
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -// Output is a closure which captures conn and calls conn.Write
 | 
	
		
			
				|  |  | -type Output func(buf []byte)
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -/* encode 8 bits unsigned int */
 | 
	
		
			
				|  |  | -func ikcp_encode8u(p []byte, c byte) []byte {
 | 
	
		
			
				|  |  | -	p[0] = c
 | 
	
		
			
				|  |  | -	return p[1:]
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -/* decode 8 bits unsigned int */
 | 
	
		
			
				|  |  | -func ikcp_decode8u(p []byte, c *byte) []byte {
 | 
	
		
			
				|  |  | -	*c = p[0]
 | 
	
		
			
				|  |  | -	return p[1:]
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -/* encode 16 bits unsigned int (lsb) */
 | 
	
		
			
				|  |  | -func ikcp_encode16u(p []byte, w uint16) []byte {
 | 
	
		
			
				|  |  | -	binary.LittleEndian.PutUint16(p, w)
 | 
	
		
			
				|  |  | -	return p[2:]
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -/* decode 16 bits unsigned int (lsb) */
 | 
	
		
			
				|  |  | -func ikcp_decode16u(p []byte, w *uint16) []byte {
 | 
	
		
			
				|  |  | -	*w = binary.LittleEndian.Uint16(p)
 | 
	
		
			
				|  |  | -	return p[2:]
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -/* encode 32 bits unsigned int (lsb) */
 | 
	
		
			
				|  |  | -func ikcp_encode32u(p []byte, l uint32) []byte {
 | 
	
		
			
				|  |  | -	binary.LittleEndian.PutUint32(p, l)
 | 
	
		
			
				|  |  | -	return p[4:]
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -/* decode 32 bits unsigned int (lsb) */
 | 
	
		
			
				|  |  | -func ikcp_decode32u(p []byte, l *uint32) []byte {
 | 
	
		
			
				|  |  | -	*l = binary.LittleEndian.Uint32(p)
 | 
	
		
			
				|  |  | -	return p[4:]
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |  func _imin_(a, b uint32) uint32 {
 | 
	
		
			
				|  |  |  	if a <= b {
 | 
	
		
			
				|  |  |  		return a
 | 
	
	
		
			
				|  | @@ -90,49 +51,22 @@ func _itimediff(later, earlier uint32) int32 {
 | 
	
		
			
				|  |  |  	return (int32)(later - earlier)
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -// Segment defines a KCP segment
 | 
	
		
			
				|  |  | -type Segment struct {
 | 
	
		
			
				|  |  | -	conv     uint32
 | 
	
		
			
				|  |  | -	cmd      uint32
 | 
	
		
			
				|  |  | -	frg      uint32
 | 
	
		
			
				|  |  | -	wnd      uint32
 | 
	
		
			
				|  |  | -	ts       uint32
 | 
	
		
			
				|  |  | -	sn       uint32
 | 
	
		
			
				|  |  | -	una      uint32
 | 
	
		
			
				|  |  | -	resendts uint32
 | 
	
		
			
				|  |  | -	fastack  uint32
 | 
	
		
			
				|  |  | -	xmit     uint32
 | 
	
		
			
				|  |  | -	data     *alloc.Buffer
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -// encode a segment into buffer
 | 
	
		
			
				|  |  | -func (seg *Segment) encode(ptr []byte) []byte {
 | 
	
		
			
				|  |  | -	ptr = ikcp_encode32u(ptr, seg.conv)
 | 
	
		
			
				|  |  | -	ptr = ikcp_encode8u(ptr, uint8(seg.cmd))
 | 
	
		
			
				|  |  | -	ptr = ikcp_encode8u(ptr, uint8(seg.frg))
 | 
	
		
			
				|  |  | -	ptr = ikcp_encode16u(ptr, uint16(seg.wnd))
 | 
	
		
			
				|  |  | -	ptr = ikcp_encode32u(ptr, seg.ts)
 | 
	
		
			
				|  |  | -	ptr = ikcp_encode32u(ptr, seg.sn)
 | 
	
		
			
				|  |  | -	ptr = ikcp_encode32u(ptr, seg.una)
 | 
	
		
			
				|  |  | -	ptr = ikcp_encode16u(ptr, uint16(seg.data.Len()))
 | 
	
		
			
				|  |  | -	return ptr
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -func (this *Segment) Release() {
 | 
	
		
			
				|  |  | -	this.data.Release()
 | 
	
		
			
				|  |  | -	this.data = nil
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | +type State int
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -// NewSegment creates a KCP segment
 | 
	
		
			
				|  |  | -func NewSegment() *Segment {
 | 
	
		
			
				|  |  | -	return &Segment{
 | 
	
		
			
				|  |  | -		data: alloc.NewSmallBuffer().Clear(),
 | 
	
		
			
				|  |  | -	}
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | +const (
 | 
	
		
			
				|  |  | +	StateActive       State = 0
 | 
	
		
			
				|  |  | +	StateReadyToClose State = 1
 | 
	
		
			
				|  |  | +	StatePeerClosed   State = 2
 | 
	
		
			
				|  |  | +	StateTerminating  State = 3
 | 
	
		
			
				|  |  | +	StateTerminated   State = 4
 | 
	
		
			
				|  |  | +)
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  // KCP defines a single KCP connection
 | 
	
		
			
				|  |  |  type KCP struct {
 | 
	
		
			
				|  |  | -	conv, mtu, mss, state                  uint32
 | 
	
		
			
				|  |  | +	conv                                   uint16
 | 
	
		
			
				|  |  | +	state                                  State
 | 
	
		
			
				|  |  | +	stateBeginTime                         uint32
 | 
	
		
			
				|  |  | +	mtu, mss                               uint32
 | 
	
		
			
				|  |  |  	snd_una, snd_nxt, rcv_nxt              uint32
 | 
	
		
			
				|  |  |  	ts_recent, ts_lastack, ssthresh        uint32
 | 
	
		
			
				|  |  |  	rx_rttvar, rx_srtt, rx_rto             uint32
 | 
	
	
		
			
				|  | @@ -143,21 +77,21 @@ type KCP struct {
 | 
	
		
			
				|  |  |  	dead_link, incr                        uint32
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  	snd_queue *SendingQueue
 | 
	
		
			
				|  |  | -	rcv_queue []*Segment
 | 
	
		
			
				|  |  | -	snd_buf   []*Segment
 | 
	
		
			
				|  |  | +	rcv_queue []*DataSegment
 | 
	
		
			
				|  |  | +	snd_buf   []*DataSegment
 | 
	
		
			
				|  |  |  	rcv_buf   *ReceivingWindow
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -	acklist []uint32
 | 
	
		
			
				|  |  | +	acklist *ACKList
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  	buffer            []byte
 | 
	
		
			
				|  |  |  	fastresend        int32
 | 
	
		
			
				|  |  |  	congestionControl bool
 | 
	
		
			
				|  |  | -	output            Output
 | 
	
		
			
				|  |  | +	output            *SegmentWriter
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  // NewKCP create a new kcp control object, 'conv' must equal in two endpoint
 | 
	
		
			
				|  |  |  // from the same connection.
 | 
	
		
			
				|  |  | -func NewKCP(conv uint32, mtu uint32, sendingWindowSize uint32, receivingWindowSize uint32, sendingQueueSize uint32, output Output) *KCP {
 | 
	
		
			
				|  |  | +func NewKCP(conv uint16, mtu uint32, sendingWindowSize uint32, receivingWindowSize uint32, sendingQueueSize uint32, output v2io.Writer) *KCP {
 | 
	
		
			
				|  |  |  	kcp := new(KCP)
 | 
	
		
			
				|  |  |  	kcp.conv = conv
 | 
	
		
			
				|  |  |  	kcp.snd_wnd = sendingWindowSize
 | 
	
	
		
			
				|  | @@ -165,18 +99,51 @@ func NewKCP(conv uint32, mtu uint32, sendingWindowSize uint32, receivingWindowSi
 | 
	
		
			
				|  |  |  	kcp.rmt_wnd = IKCP_WND_RCV
 | 
	
		
			
				|  |  |  	kcp.mtu = mtu
 | 
	
		
			
				|  |  |  	kcp.mss = kcp.mtu - IKCP_OVERHEAD
 | 
	
		
			
				|  |  | -	kcp.buffer = make([]byte, (kcp.mtu+IKCP_OVERHEAD)*3)
 | 
	
		
			
				|  |  |  	kcp.rx_rto = IKCP_RTO_DEF
 | 
	
		
			
				|  |  |  	kcp.interval = IKCP_INTERVAL
 | 
	
		
			
				|  |  |  	kcp.ts_flush = IKCP_INTERVAL
 | 
	
		
			
				|  |  |  	kcp.ssthresh = IKCP_THRESH_INIT
 | 
	
		
			
				|  |  |  	kcp.dead_link = IKCP_DEADLINK
 | 
	
		
			
				|  |  | -	kcp.output = output
 | 
	
		
			
				|  |  | +	kcp.output = NewSegmentWriter(mtu, output)
 | 
	
		
			
				|  |  |  	kcp.rcv_buf = NewReceivingWindow(receivingWindowSize)
 | 
	
		
			
				|  |  |  	kcp.snd_queue = NewSendingQueue(sendingQueueSize)
 | 
	
		
			
				|  |  | +	kcp.acklist = new(ACKList)
 | 
	
		
			
				|  |  |  	return kcp
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +func (kcp *KCP) HandleOption(opt SegmentOption) {
 | 
	
		
			
				|  |  | +	if (opt & SegmentOptionClose) == SegmentOptionClose {
 | 
	
		
			
				|  |  | +		kcp.OnPeerClosed()
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +func (kcp *KCP) OnPeerClosed() {
 | 
	
		
			
				|  |  | +	if kcp.state == StateReadyToClose {
 | 
	
		
			
				|  |  | +		kcp.state = StateTerminating
 | 
	
		
			
				|  |  | +		kcp.stateBeginTime = kcp.current
 | 
	
		
			
				|  |  | +		log.Info("KCP terminating at ", kcp.current)
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +	if kcp.state == StateActive {
 | 
	
		
			
				|  |  | +		kcp.ClearSendQueue()
 | 
	
		
			
				|  |  | +		kcp.state = StatePeerClosed
 | 
	
		
			
				|  |  | +		kcp.stateBeginTime = kcp.current
 | 
	
		
			
				|  |  | +		log.Info("KCP peer close at ", kcp.current)
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +func (kcp *KCP) OnClose() {
 | 
	
		
			
				|  |  | +	if kcp.state == StateActive {
 | 
	
		
			
				|  |  | +		kcp.state = StateReadyToClose
 | 
	
		
			
				|  |  | +		kcp.stateBeginTime = kcp.current
 | 
	
		
			
				|  |  | +		log.Info("KCP ready close at ", kcp.current)
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +	if kcp.state == StatePeerClosed {
 | 
	
		
			
				|  |  | +		kcp.state = StateTerminating
 | 
	
		
			
				|  |  | +		kcp.stateBeginTime = kcp.current
 | 
	
		
			
				|  |  | +		log.Info("KCP terminating at ", kcp.current)
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  // Recv is user/upper level recv: returns size, returns below zero for EAGAIN
 | 
	
		
			
				|  |  |  func (kcp *KCP) Recv(buffer []byte) (n int) {
 | 
	
		
			
				|  |  |  	if len(kcp.rcv_queue) == 0 {
 | 
	
	
		
			
				|  | @@ -186,11 +153,11 @@ func (kcp *KCP) Recv(buffer []byte) (n int) {
 | 
	
		
			
				|  |  |  	// merge fragment
 | 
	
		
			
				|  |  |  	count := 0
 | 
	
		
			
				|  |  |  	for _, seg := range kcp.rcv_queue {
 | 
	
		
			
				|  |  | -		dataLen := seg.data.Len()
 | 
	
		
			
				|  |  | +		dataLen := seg.Data.Len()
 | 
	
		
			
				|  |  |  		if dataLen > len(buffer) {
 | 
	
		
			
				|  |  |  			break
 | 
	
		
			
				|  |  |  		}
 | 
	
		
			
				|  |  | -		copy(buffer, seg.data.Value)
 | 
	
		
			
				|  |  | +		copy(buffer, seg.Data.Value)
 | 
	
		
			
				|  |  |  		seg.Release()
 | 
	
		
			
				|  |  |  		buffer = buffer[dataLen:]
 | 
	
		
			
				|  |  |  		n += dataLen
 | 
	
	
		
			
				|  | @@ -226,8 +193,9 @@ func (kcp *KCP) Send(buffer []byte) int {
 | 
	
		
			
				|  |  |  		} else {
 | 
	
		
			
				|  |  |  			size = len(buffer)
 | 
	
		
			
				|  |  |  		}
 | 
	
		
			
				|  |  | -		seg := NewSegment()
 | 
	
		
			
				|  |  | -		seg.data.Append(buffer[:size])
 | 
	
		
			
				|  |  | +		seg := &DataSegment{
 | 
	
		
			
				|  |  | +			Data: alloc.NewSmallBuffer().Clear().Append(buffer[:size]),
 | 
	
		
			
				|  |  | +		}
 | 
	
		
			
				|  |  |  		kcp.snd_queue.Push(seg)
 | 
	
		
			
				|  |  |  		buffer = buffer[size:]
 | 
	
		
			
				|  |  |  		nBytes += size
 | 
	
	
		
			
				|  | @@ -262,7 +230,7 @@ func (kcp *KCP) update_ack(rtt int32) {
 | 
	
		
			
				|  |  |  func (kcp *KCP) shrink_buf() {
 | 
	
		
			
				|  |  |  	if len(kcp.snd_buf) > 0 {
 | 
	
		
			
				|  |  |  		seg := kcp.snd_buf[0]
 | 
	
		
			
				|  |  | -		kcp.snd_una = seg.sn
 | 
	
		
			
				|  |  | +		kcp.snd_una = seg.Number
 | 
	
		
			
				|  |  |  	} else {
 | 
	
		
			
				|  |  |  		kcp.snd_una = kcp.snd_nxt
 | 
	
		
			
				|  |  |  	}
 | 
	
	
		
			
				|  | @@ -274,12 +242,12 @@ func (kcp *KCP) parse_ack(sn uint32) {
 | 
	
		
			
				|  |  |  	}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  	for k, seg := range kcp.snd_buf {
 | 
	
		
			
				|  |  | -		if sn == seg.sn {
 | 
	
		
			
				|  |  | +		if sn == seg.Number {
 | 
	
		
			
				|  |  |  			kcp.snd_buf = append(kcp.snd_buf[:k], kcp.snd_buf[k+1:]...)
 | 
	
		
			
				|  |  |  			seg.Release()
 | 
	
		
			
				|  |  |  			break
 | 
	
		
			
				|  |  |  		}
 | 
	
		
			
				|  |  | -		if _itimediff(sn, seg.sn) < 0 {
 | 
	
		
			
				|  |  | +		if _itimediff(sn, seg.Number) < 0 {
 | 
	
		
			
				|  |  |  			break
 | 
	
		
			
				|  |  |  		}
 | 
	
		
			
				|  |  |  	}
 | 
	
	
		
			
				|  | @@ -291,18 +259,18 @@ func (kcp *KCP) parse_fastack(sn uint32) {
 | 
	
		
			
				|  |  |  	}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  	for _, seg := range kcp.snd_buf {
 | 
	
		
			
				|  |  | -		if _itimediff(sn, seg.sn) < 0 {
 | 
	
		
			
				|  |  | +		if _itimediff(sn, seg.Number) < 0 {
 | 
	
		
			
				|  |  |  			break
 | 
	
		
			
				|  |  | -		} else if sn != seg.sn {
 | 
	
		
			
				|  |  | -			seg.fastack++
 | 
	
		
			
				|  |  | +		} else if sn != seg.Number {
 | 
	
		
			
				|  |  | +			seg.ackSkipped++
 | 
	
		
			
				|  |  |  		}
 | 
	
		
			
				|  |  |  	}
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -func (kcp *KCP) parse_una(una uint32) {
 | 
	
		
			
				|  |  | +func (kcp *KCP) HandleReceivingNext(receivingNext uint32) {
 | 
	
		
			
				|  |  |  	count := 0
 | 
	
		
			
				|  |  |  	for _, seg := range kcp.snd_buf {
 | 
	
		
			
				|  |  | -		if _itimediff(una, seg.sn) > 0 {
 | 
	
		
			
				|  |  | +		if _itimediff(receivingNext, seg.Number) > 0 {
 | 
	
		
			
				|  |  |  			seg.Release()
 | 
	
		
			
				|  |  |  			count++
 | 
	
		
			
				|  |  |  		} else {
 | 
	
	
		
			
				|  | @@ -312,17 +280,12 @@ func (kcp *KCP) parse_una(una uint32) {
 | 
	
		
			
				|  |  |  	kcp.snd_buf = kcp.snd_buf[count:]
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -// ack append
 | 
	
		
			
				|  |  | -func (kcp *KCP) ack_push(sn, ts uint32) {
 | 
	
		
			
				|  |  | -	kcp.acklist = append(kcp.acklist, sn, ts)
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -func (kcp *KCP) ack_get(p int) (sn, ts uint32) {
 | 
	
		
			
				|  |  | -	return kcp.acklist[p*2+0], kcp.acklist[p*2+1]
 | 
	
		
			
				|  |  | +func (kcp *KCP) HandleSendingNext(sendingNext uint32) {
 | 
	
		
			
				|  |  | +	kcp.acklist.Clear(sendingNext)
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -func (kcp *KCP) parse_data(newseg *Segment) {
 | 
	
		
			
				|  |  | -	sn := newseg.sn
 | 
	
		
			
				|  |  | +func (kcp *KCP) parse_data(newseg *DataSegment) {
 | 
	
		
			
				|  |  | +	sn := newseg.Number
 | 
	
		
			
				|  |  |  	if _itimediff(sn, kcp.rcv_nxt+kcp.rcv_wnd) >= 0 ||
 | 
	
		
			
				|  |  |  		_itimediff(sn, kcp.rcv_nxt) < 0 {
 | 
	
		
			
				|  |  |  		return
 | 
	
	
		
			
				|  | @@ -338,163 +301,132 @@ func (kcp *KCP) parse_data(newseg *Segment) {
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  // Input when you received a low level packet (eg. UDP packet), call it
 | 
	
		
			
				|  |  |  func (kcp *KCP) Input(data []byte) int {
 | 
	
		
			
				|  |  | -	//una := kcp.snd_una
 | 
	
		
			
				|  |  | -	if len(data) < IKCP_OVERHEAD {
 | 
	
		
			
				|  |  | -		return -1
 | 
	
		
			
				|  |  | -	}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | +	log.Info("KCP input at ", kcp.current)
 | 
	
		
			
				|  |  | +	var seg ISegment
 | 
	
		
			
				|  |  |  	var maxack uint32
 | 
	
		
			
				|  |  |  	var flag int
 | 
	
		
			
				|  |  |  	for {
 | 
	
		
			
				|  |  | -		var ts, sn, una, conv uint32
 | 
	
		
			
				|  |  | -		var wnd, length uint16
 | 
	
		
			
				|  |  | -		var cmd, frg uint8
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -		if len(data) < int(IKCP_OVERHEAD) {
 | 
	
		
			
				|  |  | +		seg, data = ReadSegment(data)
 | 
	
		
			
				|  |  | +		if seg == nil {
 | 
	
		
			
				|  |  |  			break
 | 
	
		
			
				|  |  |  		}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -		data = ikcp_decode32u(data, &conv)
 | 
	
		
			
				|  |  | -		if conv != kcp.conv {
 | 
	
		
			
				|  |  | -			return -1
 | 
	
		
			
				|  |  | -		}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -		data = ikcp_decode8u(data, &cmd)
 | 
	
		
			
				|  |  | -		data = ikcp_decode8u(data, &frg)
 | 
	
		
			
				|  |  | -		data = ikcp_decode16u(data, &wnd)
 | 
	
		
			
				|  |  | -		data = ikcp_decode32u(data, &ts)
 | 
	
		
			
				|  |  | -		data = ikcp_decode32u(data, &sn)
 | 
	
		
			
				|  |  | -		data = ikcp_decode32u(data, &una)
 | 
	
		
			
				|  |  | -		data = ikcp_decode16u(data, &length)
 | 
	
		
			
				|  |  | -		if len(data) < int(length) {
 | 
	
		
			
				|  |  | -			return -2
 | 
	
		
			
				|  |  | -		}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -		if cmd != IKCP_CMD_PUSH && cmd != IKCP_CMD_ACK {
 | 
	
		
			
				|  |  | -			return -3
 | 
	
		
			
				|  |  | -		}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -		if kcp.rmt_wnd < uint32(wnd) {
 | 
	
		
			
				|  |  | -			kcp.rmt_wnd = uint32(wnd)
 | 
	
		
			
				|  |  | -		}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -		kcp.parse_una(una)
 | 
	
		
			
				|  |  | -		kcp.shrink_buf()
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -		if cmd == IKCP_CMD_ACK {
 | 
	
		
			
				|  |  | -			if _itimediff(kcp.current, ts) >= 0 {
 | 
	
		
			
				|  |  | -				kcp.update_ack(_itimediff(kcp.current, ts))
 | 
	
		
			
				|  |  | -			}
 | 
	
		
			
				|  |  | -			kcp.parse_ack(sn)
 | 
	
		
			
				|  |  | +		switch seg := seg.(type) {
 | 
	
		
			
				|  |  | +		case *DataSegment:
 | 
	
		
			
				|  |  | +			kcp.HandleOption(seg.Opt)
 | 
	
		
			
				|  |  | +			kcp.HandleSendingNext(seg.SendingNext)
 | 
	
		
			
				|  |  |  			kcp.shrink_buf()
 | 
	
		
			
				|  |  | -			if flag == 0 {
 | 
	
		
			
				|  |  | -				flag = 1
 | 
	
		
			
				|  |  | -				maxack = sn
 | 
	
		
			
				|  |  | -			} else if _itimediff(sn, maxack) > 0 {
 | 
	
		
			
				|  |  | -				maxack = sn
 | 
	
		
			
				|  |  | +			kcp.acklist.Add(seg.Number, seg.Timestamp)
 | 
	
		
			
				|  |  | +			kcp.parse_data(seg)
 | 
	
		
			
				|  |  | +		case *ACKSegment:
 | 
	
		
			
				|  |  | +			kcp.HandleOption(seg.Opt)
 | 
	
		
			
				|  |  | +			if kcp.rmt_wnd < seg.ReceivingWindow {
 | 
	
		
			
				|  |  | +				kcp.rmt_wnd = seg.ReceivingWindow
 | 
	
		
			
				|  |  |  			}
 | 
	
		
			
				|  |  | -		} else if cmd == IKCP_CMD_PUSH {
 | 
	
		
			
				|  |  | -			if _itimediff(sn, kcp.rcv_nxt+kcp.rcv_wnd) < 0 {
 | 
	
		
			
				|  |  | -				kcp.ack_push(sn, ts)
 | 
	
		
			
				|  |  | -				if _itimediff(sn, kcp.rcv_nxt) >= 0 {
 | 
	
		
			
				|  |  | -					seg := NewSegment()
 | 
	
		
			
				|  |  | -					seg.conv = conv
 | 
	
		
			
				|  |  | -					seg.cmd = uint32(cmd)
 | 
	
		
			
				|  |  | -					seg.frg = uint32(frg)
 | 
	
		
			
				|  |  | -					seg.wnd = uint32(wnd)
 | 
	
		
			
				|  |  | -					seg.ts = ts
 | 
	
		
			
				|  |  | -					seg.sn = sn
 | 
	
		
			
				|  |  | -					seg.una = una
 | 
	
		
			
				|  |  | -					seg.data.Append(data[:length])
 | 
	
		
			
				|  |  | -					kcp.parse_data(seg)
 | 
	
		
			
				|  |  | +			kcp.HandleReceivingNext(seg.ReceivingNext)
 | 
	
		
			
				|  |  | +			for i := 0; i < int(seg.Count); i++ {
 | 
	
		
			
				|  |  | +				ts := seg.TimestampList[i]
 | 
	
		
			
				|  |  | +				sn := seg.NumberList[i]
 | 
	
		
			
				|  |  | +				if _itimediff(kcp.current, ts) >= 0 {
 | 
	
		
			
				|  |  | +					kcp.update_ack(_itimediff(kcp.current, ts))
 | 
	
		
			
				|  |  | +				}
 | 
	
		
			
				|  |  | +				kcp.parse_ack(sn)
 | 
	
		
			
				|  |  | +				if flag == 0 {
 | 
	
		
			
				|  |  | +					flag = 1
 | 
	
		
			
				|  |  | +					maxack = sn
 | 
	
		
			
				|  |  | +				} else if _itimediff(sn, maxack) > 0 {
 | 
	
		
			
				|  |  | +					maxack = sn
 | 
	
		
			
				|  |  |  				}
 | 
	
		
			
				|  |  |  			}
 | 
	
		
			
				|  |  | -		} else {
 | 
	
		
			
				|  |  | -			return -3
 | 
	
		
			
				|  |  | +			kcp.shrink_buf()
 | 
	
		
			
				|  |  | +		case *CmdOnlySegment:
 | 
	
		
			
				|  |  | +			kcp.HandleOption(seg.Opt)
 | 
	
		
			
				|  |  | +			if seg.Cmd == SegmentCommandTerminated {
 | 
	
		
			
				|  |  | +				if kcp.state == StateActive ||
 | 
	
		
			
				|  |  | +					kcp.state == StateReadyToClose ||
 | 
	
		
			
				|  |  | +					kcp.state == StatePeerClosed {
 | 
	
		
			
				|  |  | +					kcp.state = StateTerminating
 | 
	
		
			
				|  |  | +					kcp.stateBeginTime = kcp.current
 | 
	
		
			
				|  |  | +					log.Info("KCP terminating at ", kcp.current)
 | 
	
		
			
				|  |  | +				} else if kcp.state == StateTerminating {
 | 
	
		
			
				|  |  | +					kcp.state = StateTerminated
 | 
	
		
			
				|  |  | +					kcp.stateBeginTime = kcp.current
 | 
	
		
			
				|  |  | +					log.Info("KCP terminated at ", kcp.current)
 | 
	
		
			
				|  |  | +				}
 | 
	
		
			
				|  |  | +			}
 | 
	
		
			
				|  |  | +			kcp.HandleReceivingNext(seg.ReceivinNext)
 | 
	
		
			
				|  |  | +			kcp.HandleSendingNext(seg.SendingNext)
 | 
	
		
			
				|  |  | +		default:
 | 
	
		
			
				|  |  |  		}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -		data = data[length:]
 | 
	
		
			
				|  |  |  	}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  	if flag != 0 {
 | 
	
		
			
				|  |  |  		kcp.parse_fastack(maxack)
 | 
	
		
			
				|  |  |  	}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -	/*
 | 
	
		
			
				|  |  | -		if _itimediff(kcp.snd_una, una) > 0 {
 | 
	
		
			
				|  |  | -			if kcp.cwnd < kcp.rmt_wnd {
 | 
	
		
			
				|  |  | -				mss := kcp.mss
 | 
	
		
			
				|  |  | -				if kcp.cwnd < kcp.ssthresh {
 | 
	
		
			
				|  |  | -					kcp.cwnd++
 | 
	
		
			
				|  |  | -					kcp.incr += mss
 | 
	
		
			
				|  |  | -				} else {
 | 
	
		
			
				|  |  | -					if kcp.incr < mss {
 | 
	
		
			
				|  |  | -						kcp.incr = mss
 | 
	
		
			
				|  |  | -					}
 | 
	
		
			
				|  |  | -					kcp.incr += (mss*mss)/kcp.incr + (mss / 16)
 | 
	
		
			
				|  |  | -					if (kcp.cwnd+1)*mss <= kcp.incr {
 | 
	
		
			
				|  |  | -						kcp.cwnd++
 | 
	
		
			
				|  |  | -					}
 | 
	
		
			
				|  |  | -				}
 | 
	
		
			
				|  |  | -				if kcp.cwnd > kcp.rmt_wnd {
 | 
	
		
			
				|  |  | -					kcp.cwnd = kcp.rmt_wnd
 | 
	
		
			
				|  |  | -					kcp.incr = kcp.rmt_wnd * mss
 | 
	
		
			
				|  |  | -				}
 | 
	
		
			
				|  |  | -			}
 | 
	
		
			
				|  |  | -		}*/
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |  	return 0
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  // flush pending data
 | 
	
		
			
				|  |  |  func (kcp *KCP) flush() {
 | 
	
		
			
				|  |  | +	if kcp.state == StateTerminated {
 | 
	
		
			
				|  |  | +		return
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +	if kcp.state == StateTerminating {
 | 
	
		
			
				|  |  | +		kcp.output.Write(&CmdOnlySegment{
 | 
	
		
			
				|  |  | +			Conv: kcp.conv,
 | 
	
		
			
				|  |  | +			Cmd:  SegmentCommandTerminated,
 | 
	
		
			
				|  |  | +		})
 | 
	
		
			
				|  |  | +		kcp.output.Flush()
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +		if _itimediff(kcp.current, kcp.stateBeginTime) > 8000 {
 | 
	
		
			
				|  |  | +			kcp.state = StateTerminated
 | 
	
		
			
				|  |  | +			log.Info("KCP terminated at ", kcp.current)
 | 
	
		
			
				|  |  | +			kcp.stateBeginTime = kcp.current
 | 
	
		
			
				|  |  | +		}
 | 
	
		
			
				|  |  | +		return
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	if kcp.state == StateReadyToClose && _itimediff(kcp.current, kcp.stateBeginTime) > 15000 {
 | 
	
		
			
				|  |  | +		kcp.state = StateTerminating
 | 
	
		
			
				|  |  | +		log.Info("KCP terminating at ", kcp.current)
 | 
	
		
			
				|  |  | +		kcp.stateBeginTime = kcp.current
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  	current := kcp.current
 | 
	
		
			
				|  |  | -	buffer := kcp.buffer
 | 
	
		
			
				|  |  | -	change := 0
 | 
	
		
			
				|  |  | +	segSent := false
 | 
	
		
			
				|  |  |  	//lost := false
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -	if !kcp.updated {
 | 
	
		
			
				|  |  | -		return
 | 
	
		
			
				|  |  | -	}
 | 
	
		
			
				|  |  | -	var seg Segment
 | 
	
		
			
				|  |  | -	seg.conv = kcp.conv
 | 
	
		
			
				|  |  | -	seg.cmd = IKCP_CMD_ACK
 | 
	
		
			
				|  |  | -	seg.wnd = uint32(kcp.rcv_nxt + kcp.rcv_wnd)
 | 
	
		
			
				|  |  | -	seg.una = kcp.rcv_nxt
 | 
	
		
			
				|  |  | +	//var seg Segment
 | 
	
		
			
				|  |  | +	//seg.conv = kcp.conv
 | 
	
		
			
				|  |  | +	//seg.cmd = IKCP_CMD_ACK
 | 
	
		
			
				|  |  | +	//seg.wnd = uint32(kcp.rcv_nxt + kcp.rcv_wnd)
 | 
	
		
			
				|  |  | +	//seg.una = kcp.rcv_nxt
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  	// flush acknowledges
 | 
	
		
			
				|  |  | -	count := len(kcp.acklist) / 2
 | 
	
		
			
				|  |  | -	ptr := buffer
 | 
	
		
			
				|  |  | -	for i := 0; i < count; i++ {
 | 
	
		
			
				|  |  | -		size := len(buffer) - len(ptr)
 | 
	
		
			
				|  |  | -		if size+IKCP_OVERHEAD > int(kcp.mtu) {
 | 
	
		
			
				|  |  | -			kcp.output(buffer[:size])
 | 
	
		
			
				|  |  | -			ptr = buffer
 | 
	
		
			
				|  |  | -		}
 | 
	
		
			
				|  |  | -		seg.sn, seg.ts = kcp.ack_get(i)
 | 
	
		
			
				|  |  | -		ptr = seg.encode(ptr)
 | 
	
		
			
				|  |  | +	ackSeg := kcp.acklist.AsSegment()
 | 
	
		
			
				|  |  | +	if ackSeg != nil {
 | 
	
		
			
				|  |  | +		ackSeg.Conv = kcp.conv
 | 
	
		
			
				|  |  | +		ackSeg.ReceivingWindow = uint32(kcp.rcv_nxt + kcp.rcv_wnd)
 | 
	
		
			
				|  |  | +		ackSeg.ReceivingNext = kcp.rcv_nxt
 | 
	
		
			
				|  |  | +		kcp.output.Write(ackSeg)
 | 
	
		
			
				|  |  | +		segSent = true
 | 
	
		
			
				|  |  |  	}
 | 
	
		
			
				|  |  | -	kcp.acklist = nil
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  	// calculate window size
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |  	cwnd := _imin_(kcp.snd_una+kcp.snd_wnd, kcp.rmt_wnd)
 | 
	
		
			
				|  |  |  	if kcp.congestionControl {
 | 
	
		
			
				|  |  |  		cwnd = _imin_(kcp.cwnd, cwnd)
 | 
	
		
			
				|  |  |  	}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  	for !kcp.snd_queue.IsEmpty() && _itimediff(kcp.snd_nxt, cwnd) < 0 {
 | 
	
		
			
				|  |  | -		newseg := kcp.snd_queue.Pop()
 | 
	
		
			
				|  |  | -		newseg.conv = kcp.conv
 | 
	
		
			
				|  |  | -		newseg.cmd = IKCP_CMD_PUSH
 | 
	
		
			
				|  |  | -		newseg.wnd = seg.wnd
 | 
	
		
			
				|  |  | -		newseg.ts = current
 | 
	
		
			
				|  |  | -		newseg.sn = kcp.snd_nxt
 | 
	
		
			
				|  |  | -		newseg.una = kcp.rcv_nxt
 | 
	
		
			
				|  |  | -		newseg.resendts = current
 | 
	
		
			
				|  |  | -		newseg.fastack = 0
 | 
	
		
			
				|  |  | -		newseg.xmit = 0
 | 
	
		
			
				|  |  | -		kcp.snd_buf = append(kcp.snd_buf, newseg)
 | 
	
		
			
				|  |  | +		seg := kcp.snd_queue.Pop()
 | 
	
		
			
				|  |  | +		seg.Conv = kcp.conv
 | 
	
		
			
				|  |  | +		seg.Number = kcp.snd_nxt
 | 
	
		
			
				|  |  | +		seg.timeout = current
 | 
	
		
			
				|  |  | +		seg.ackSkipped = 0
 | 
	
		
			
				|  |  | +		seg.transmit = 0
 | 
	
		
			
				|  |  | +		kcp.snd_buf = append(kcp.snd_buf, seg)
 | 
	
		
			
				|  |  |  		kcp.snd_nxt++
 | 
	
		
			
				|  |  |  	}
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -507,51 +439,75 @@ func (kcp *KCP) flush() {
 | 
	
		
			
				|  |  |  	// flush data segments
 | 
	
		
			
				|  |  |  	for _, segment := range kcp.snd_buf {
 | 
	
		
			
				|  |  |  		needsend := false
 | 
	
		
			
				|  |  | -		if segment.xmit == 0 {
 | 
	
		
			
				|  |  | +		if segment.transmit == 0 {
 | 
	
		
			
				|  |  |  			needsend = true
 | 
	
		
			
				|  |  | -			segment.xmit++
 | 
	
		
			
				|  |  | -			segment.resendts = current + kcp.rx_rto
 | 
	
		
			
				|  |  | -		} else if _itimediff(current, segment.resendts) >= 0 {
 | 
	
		
			
				|  |  | +			segment.transmit++
 | 
	
		
			
				|  |  | +			segment.timeout = current + kcp.rx_rto
 | 
	
		
			
				|  |  | +		} else if _itimediff(current, segment.timeout) >= 0 {
 | 
	
		
			
				|  |  |  			needsend = true
 | 
	
		
			
				|  |  | -			segment.xmit++
 | 
	
		
			
				|  |  | +			segment.transmit++
 | 
	
		
			
				|  |  |  			kcp.xmit++
 | 
	
		
			
				|  |  | -			segment.resendts = current + kcp.rx_rto
 | 
	
		
			
				|  |  | +			segment.timeout = current + kcp.rx_rto
 | 
	
		
			
				|  |  |  			//lost = true
 | 
	
		
			
				|  |  | -		} else if segment.fastack >= resent {
 | 
	
		
			
				|  |  | +		} else if segment.ackSkipped >= resent {
 | 
	
		
			
				|  |  |  			needsend = true
 | 
	
		
			
				|  |  | -			segment.xmit++
 | 
	
		
			
				|  |  | -			segment.fastack = 0
 | 
	
		
			
				|  |  | -			segment.resendts = current + kcp.rx_rto
 | 
	
		
			
				|  |  | -			change++
 | 
	
		
			
				|  |  | +			segment.transmit++
 | 
	
		
			
				|  |  | +			segment.ackSkipped = 0
 | 
	
		
			
				|  |  | +			segment.timeout = current + kcp.rx_rto
 | 
	
		
			
				|  |  |  		}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  		if needsend {
 | 
	
		
			
				|  |  | -			segment.ts = current
 | 
	
		
			
				|  |  | -			segment.wnd = seg.wnd
 | 
	
		
			
				|  |  | -			segment.una = kcp.rcv_nxt
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -			size := len(buffer) - len(ptr)
 | 
	
		
			
				|  |  | -			need := IKCP_OVERHEAD + segment.data.Len()
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -			if size+need >= int(kcp.mtu) {
 | 
	
		
			
				|  |  | -				kcp.output(buffer[:size])
 | 
	
		
			
				|  |  | -				ptr = buffer
 | 
	
		
			
				|  |  | +			segment.Timestamp = current
 | 
	
		
			
				|  |  | +			segment.SendingNext = kcp.snd_una
 | 
	
		
			
				|  |  | +			segment.Opt = 0
 | 
	
		
			
				|  |  | +			if kcp.state == StateReadyToClose {
 | 
	
		
			
				|  |  | +				segment.Opt = SegmentOptionClose
 | 
	
		
			
				|  |  |  			}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -			ptr = segment.encode(ptr)
 | 
	
		
			
				|  |  | -			copy(ptr, segment.data.Value)
 | 
	
		
			
				|  |  | -			ptr = ptr[segment.data.Len():]
 | 
	
		
			
				|  |  | +			kcp.output.Write(segment)
 | 
	
		
			
				|  |  | +			segSent = true
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -			if segment.xmit >= kcp.dead_link {
 | 
	
		
			
				|  |  | +			if segment.transmit >= kcp.dead_link {
 | 
	
		
			
				|  |  |  				kcp.state = 0xFFFFFFFF
 | 
	
		
			
				|  |  |  			}
 | 
	
		
			
				|  |  |  		}
 | 
	
		
			
				|  |  |  	}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  	// flash remain segments
 | 
	
		
			
				|  |  | -	size := len(buffer) - len(ptr)
 | 
	
		
			
				|  |  | -	if size > 0 {
 | 
	
		
			
				|  |  | -		kcp.output(buffer[:size])
 | 
	
		
			
				|  |  | +	kcp.output.Flush()
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	if !segSent && kcp.state == StateReadyToClose {
 | 
	
		
			
				|  |  | +		kcp.output.Write(&CmdOnlySegment{
 | 
	
		
			
				|  |  | +			Conv:         kcp.conv,
 | 
	
		
			
				|  |  | +			Cmd:          SegmentCommandPing,
 | 
	
		
			
				|  |  | +			Opt:          SegmentOptionClose,
 | 
	
		
			
				|  |  | +			ReceivinNext: kcp.rcv_nxt,
 | 
	
		
			
				|  |  | +			SendingNext:  kcp.snd_nxt,
 | 
	
		
			
				|  |  | +		})
 | 
	
		
			
				|  |  | +		kcp.output.Flush()
 | 
	
		
			
				|  |  | +		segSent = true
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	if !segSent && kcp.state == StateTerminating {
 | 
	
		
			
				|  |  | +		kcp.output.Write(&CmdOnlySegment{
 | 
	
		
			
				|  |  | +			Conv:         kcp.conv,
 | 
	
		
			
				|  |  | +			Cmd:          SegmentCommandTerminated,
 | 
	
		
			
				|  |  | +			ReceivinNext: kcp.rcv_nxt,
 | 
	
		
			
				|  |  | +			SendingNext:  kcp.snd_una,
 | 
	
		
			
				|  |  | +		})
 | 
	
		
			
				|  |  | +		kcp.output.Flush()
 | 
	
		
			
				|  |  | +		segSent = true
 | 
	
		
			
				|  |  | +	}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	if !segSent {
 | 
	
		
			
				|  |  | +		kcp.output.Write(&CmdOnlySegment{
 | 
	
		
			
				|  |  | +			Conv:         kcp.conv,
 | 
	
		
			
				|  |  | +			Cmd:          SegmentCommandPing,
 | 
	
		
			
				|  |  | +			ReceivinNext: kcp.rcv_nxt,
 | 
	
		
			
				|  |  | +			SendingNext:  kcp.snd_una,
 | 
	
		
			
				|  |  | +		})
 | 
	
		
			
				|  |  | +		kcp.output.Flush()
 | 
	
		
			
				|  |  | +		segSent = true
 | 
	
		
			
				|  |  |  	}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  	// update ssthresh
 | 
	
	
		
			
				|  | @@ -613,54 +569,6 @@ func (kcp *KCP) Update(current uint32) {
 | 
	
		
			
				|  |  |  	}
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -// Check determines when should you invoke ikcp_update:
 | 
	
		
			
				|  |  | -// returns when you should invoke ikcp_update in millisec, if there
 | 
	
		
			
				|  |  | -// is no ikcp_input/_send calling. you can call ikcp_update in that
 | 
	
		
			
				|  |  | -// time, instead of call update repeatly.
 | 
	
		
			
				|  |  | -// Important to reduce unnacessary ikcp_update invoking. use it to
 | 
	
		
			
				|  |  | -// schedule ikcp_update (eg. implementing an epoll-like mechanism,
 | 
	
		
			
				|  |  | -// or optimize ikcp_update when handling massive kcp connections)
 | 
	
		
			
				|  |  | -func (kcp *KCP) Check(current uint32) uint32 {
 | 
	
		
			
				|  |  | -	ts_flush := kcp.ts_flush
 | 
	
		
			
				|  |  | -	tm_flush := int32(0x7fffffff)
 | 
	
		
			
				|  |  | -	tm_packet := int32(0x7fffffff)
 | 
	
		
			
				|  |  | -	minimal := uint32(0)
 | 
	
		
			
				|  |  | -	if !kcp.updated {
 | 
	
		
			
				|  |  | -		return current
 | 
	
		
			
				|  |  | -	}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -	if _itimediff(current, ts_flush) >= 10000 ||
 | 
	
		
			
				|  |  | -		_itimediff(current, ts_flush) < -10000 {
 | 
	
		
			
				|  |  | -		ts_flush = current
 | 
	
		
			
				|  |  | -	}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -	if _itimediff(current, ts_flush) >= 0 {
 | 
	
		
			
				|  |  | -		return current
 | 
	
		
			
				|  |  | -	}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -	tm_flush = _itimediff(ts_flush, current)
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -	for _, seg := range kcp.snd_buf {
 | 
	
		
			
				|  |  | -		diff := _itimediff(seg.resendts, current)
 | 
	
		
			
				|  |  | -		if diff <= 0 {
 | 
	
		
			
				|  |  | -			return current
 | 
	
		
			
				|  |  | -		}
 | 
	
		
			
				|  |  | -		if diff < tm_packet {
 | 
	
		
			
				|  |  | -			tm_packet = diff
 | 
	
		
			
				|  |  | -		}
 | 
	
		
			
				|  |  | -	}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -	minimal = uint32(tm_packet)
 | 
	
		
			
				|  |  | -	if tm_packet >= tm_flush {
 | 
	
		
			
				|  |  | -		minimal = uint32(tm_flush)
 | 
	
		
			
				|  |  | -	}
 | 
	
		
			
				|  |  | -	if minimal >= kcp.interval {
 | 
	
		
			
				|  |  | -		minimal = kcp.interval
 | 
	
		
			
				|  |  | -	}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -	return current + minimal
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |  // NoDelay options
 | 
	
		
			
				|  |  |  // fastest: ikcp_nodelay(kcp, 1, 20, 2, 1)
 | 
	
		
			
				|  |  |  // nodelay: 0:disable(default), 1:enable
 |