| 
					
				 | 
			
			
				@@ -1,563 +0,0 @@ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-package kcp 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-import ( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	crand "crypto/rand" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	"encoding/binary" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	"errors" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	"hash/crc32" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	"io" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	"log" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	"math/rand" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	"net" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	"sync" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	"time" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	"golang.org/x/net/ipv4" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-var ( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	errTimeout    = errors.New("i/o timeout") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	errBrokenPipe = errors.New("broken pipe") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-const ( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	basePort        = 20000 // minimum port for listening 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	maxPort         = 65535 // maximum port for listening 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	defaultWndSize  = 128   // default window size, in packet 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	otpSize         = 16    // magic number 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	crcSize         = 4     // 4bytes packet checksum 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	cryptHeaderSize = otpSize + crcSize 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	connTimeout     = 60 * time.Second 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	mtuLimit        = 4096 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	rxQueueLimit    = 8192 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	rxFecLimit      = 2048 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-type ( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	// UDPSession defines a KCP session implemented by UDP 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	UDPSession struct { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		kcp           *KCP         // the core ARQ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		conn          *net.UDPConn // the underlying UDP socket 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		block         BlockCrypt 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		needUpdate    bool 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		l             *Listener // point to server listener if it's a server socket 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		local, remote net.Addr 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		rd            time.Time // read deadline 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		wd            time.Time // write deadline 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		sockbuff      []byte    // kcp receiving is based on packet, I turn it into stream 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		die           chan struct{} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		isClosed      bool 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		mu            sync.Mutex 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		chReadEvent   chan struct{} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		chWriteEvent  chan struct{} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		chTicker      chan time.Time 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		chUDPOutput   chan []byte 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		headerSize    int 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		lastInputTs   time.Time 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		ackNoDelay    bool 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-// newUDPSession create a new udp session for client or server 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-func newUDPSession(conv uint32, l *Listener, conn *net.UDPConn, remote *net.UDPAddr, block BlockCrypt) *UDPSession { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	sess := new(UDPSession) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	sess.chTicker = make(chan time.Time, 1) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	sess.chUDPOutput = make(chan []byte, rxQueueLimit) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	sess.die = make(chan struct{}) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	sess.local = conn.LocalAddr() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	sess.chReadEvent = make(chan struct{}, 1) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	sess.chWriteEvent = make(chan struct{}, 1) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	sess.remote = remote 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	sess.conn = conn 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	sess.l = l 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	sess.block = block 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	sess.lastInputTs = time.Now() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	// caculate header size 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	if sess.block != nil { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		sess.headerSize += cryptHeaderSize 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	sess.kcp = NewKCP(conv, func(buf []byte, size int) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		if size >= IKCP_OVERHEAD { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			ext := make([]byte, sess.headerSize+size) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			copy(ext[sess.headerSize:], buf) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			sess.chUDPOutput <- ext 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	}) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	sess.kcp.WndSize(defaultWndSize, defaultWndSize) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	sess.kcp.SetMtu(IKCP_MTU_DEF - sess.headerSize) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	go sess.updateTask() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	go sess.outputTask() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	if l == nil { // it's a client connection 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		go sess.readLoop() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	return sess 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-// Read implements the Conn Read method. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-func (s *UDPSession) Read(b []byte) (n int, err error) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	for { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		s.mu.Lock() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		if len(s.sockbuff) > 0 { // copy from buffer 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			n = copy(b, s.sockbuff) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			s.sockbuff = s.sockbuff[n:] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			s.mu.Unlock() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			return n, nil 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		if s.isClosed { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			s.mu.Unlock() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			return 0, errBrokenPipe 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		if !s.rd.IsZero() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			if time.Now().After(s.rd) { // timeout 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				s.mu.Unlock() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				return 0, errTimeout 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		if n := s.kcp.PeekSize(); n > 0 { // data arrived 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			if len(b) >= n { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				s.kcp.Recv(b) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			} else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				buf := make([]byte, n) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				s.kcp.Recv(buf) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				n = copy(b, buf) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				s.sockbuff = buf[n:] // store remaining bytes into sockbuff for next read 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			s.mu.Unlock() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			return n, nil 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		var timeout <-chan time.Time 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		if !s.rd.IsZero() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			delay := s.rd.Sub(time.Now()) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			timeout = time.After(delay) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		s.mu.Unlock() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		// wait for read event or timeout 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		select { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		case <-s.chReadEvent: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		case <-timeout: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		case <-s.die: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-// Write implements the Conn Write method. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-func (s *UDPSession) Write(b []byte) (n int, err error) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	for { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		s.mu.Lock() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		if s.isClosed { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			s.mu.Unlock() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			return 0, errBrokenPipe 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		if !s.wd.IsZero() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			if time.Now().After(s.wd) { // timeout 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				s.mu.Unlock() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				return 0, errTimeout 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		if s.kcp.WaitSnd() < int(s.kcp.snd_wnd) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			n = len(b) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			max := s.kcp.mss << 8 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			for { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				if len(b) <= int(max) { // in most cases 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					s.kcp.Send(b) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					break 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				} else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					s.kcp.Send(b[:max]) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					b = b[max:] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			s.kcp.current = currentMs() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			s.kcp.flush() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			s.mu.Unlock() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			return n, nil 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		var timeout <-chan time.Time 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		if !s.wd.IsZero() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			delay := s.wd.Sub(time.Now()) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			timeout = time.After(delay) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		s.mu.Unlock() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		// wait for write event or timeout 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		select { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		case <-s.chWriteEvent: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		case <-timeout: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		case <-s.die: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-// Close closes the connection. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-func (s *UDPSession) Close() error { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	s.mu.Lock() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	defer s.mu.Unlock() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	if s.isClosed { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		return errBrokenPipe 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	close(s.die) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	s.isClosed = true 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	if s.l == nil { // client socket close 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		s.conn.Close() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	return nil 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-// LocalAddr returns the local network address. The Addr returned is shared by all invocations of LocalAddr, so do not modify it. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-func (s *UDPSession) LocalAddr() net.Addr { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	return s.local 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-// RemoteAddr returns the remote network address. The Addr returned is shared by all invocations of RemoteAddr, so do not modify it. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-func (s *UDPSession) RemoteAddr() net.Addr { return s.remote } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-// SetDeadline sets the deadline associated with the listener. A zero time value disables the deadline. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-func (s *UDPSession) SetDeadline(t time.Time) error { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	s.mu.Lock() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	defer s.mu.Unlock() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	s.rd = t 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	s.wd = t 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	return nil 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-// SetReadDeadline implements the Conn SetReadDeadline method. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-func (s *UDPSession) SetReadDeadline(t time.Time) error { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	s.mu.Lock() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	defer s.mu.Unlock() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	s.rd = t 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	return nil 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-// SetWriteDeadline implements the Conn SetWriteDeadline method. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-func (s *UDPSession) SetWriteDeadline(t time.Time) error { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	s.mu.Lock() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	defer s.mu.Unlock() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	s.wd = t 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	return nil 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-// SetWindowSize set maximum window size 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-func (s *UDPSession) SetWindowSize(sndwnd, rcvwnd int) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	s.mu.Lock() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	defer s.mu.Unlock() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	s.kcp.WndSize(sndwnd, rcvwnd) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-// SetMtu sets the maximum transmission unit 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-func (s *UDPSession) SetMtu(mtu int) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	s.mu.Lock() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	defer s.mu.Unlock() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	s.kcp.SetMtu(mtu - s.headerSize) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-// SetACKNoDelay changes ack flush option, set true to flush ack immediately, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-func (s *UDPSession) SetACKNoDelay(nodelay bool) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	s.mu.Lock() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	defer s.mu.Unlock() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	s.ackNoDelay = nodelay 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-// SetNoDelay calls nodelay() of kcp 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-func (s *UDPSession) SetNoDelay(nodelay, interval, resend, nc int) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	s.mu.Lock() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	defer s.mu.Unlock() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	s.kcp.NoDelay(nodelay, interval, resend, nc) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-// SetDSCP sets the DSCP field of IP header 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-func (s *UDPSession) SetDSCP(tos int) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	s.mu.Lock() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	defer s.mu.Unlock() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	if err := ipv4.NewConn(s.conn).SetTOS(tos << 2); err != nil { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		log.Println("set tos:", err) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-func (s *UDPSession) outputTask() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	// ping 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	ticker := time.NewTicker(5 * time.Second) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	defer ticker.Stop() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	for { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		select { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		case ext := <-s.chUDPOutput: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			if s.block != nil { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				io.ReadFull(crand.Reader, ext[:otpSize]) // OTP 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				checksum := crc32.ChecksumIEEE(ext[cryptHeaderSize:]) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				binary.LittleEndian.PutUint32(ext[otpSize:], checksum) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				s.block.Encrypt(ext, ext) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			//if rand.Intn(100) < 80 { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			n, err := s.conn.WriteTo(ext, s.remote) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			if err != nil { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				log.Println(err, n) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			//} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		case <-ticker.C: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			sz := rand.Intn(IKCP_MTU_DEF - s.headerSize - IKCP_OVERHEAD) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			sz += s.headerSize + IKCP_OVERHEAD 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			ping := make([]byte, sz) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			io.ReadFull(crand.Reader, ping) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			if s.block != nil { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				checksum := crc32.ChecksumIEEE(ping[cryptHeaderSize:]) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				binary.LittleEndian.PutUint32(ping[otpSize:], checksum) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				s.block.Encrypt(ping, ping) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			n, err := s.conn.WriteTo(ping, s.remote) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			if err != nil { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				log.Println(err, n) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		case <-s.die: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			return 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-// kcp update, input loop 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-func (s *UDPSession) updateTask() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	var tc <-chan time.Time 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	if s.l == nil { // client 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		ticker := time.NewTicker(10 * time.Millisecond) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		tc = ticker.C 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		defer ticker.Stop() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	} else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		tc = s.chTicker 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	var nextupdate uint32 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	for { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		select { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		case <-tc: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			s.mu.Lock() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			current := currentMs() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			if current >= nextupdate || s.needUpdate { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				s.kcp.Update(current) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				nextupdate = s.kcp.Check(current) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			if s.kcp.WaitSnd() < int(s.kcp.snd_wnd) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				s.notifyWriteEvent() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			s.needUpdate = false 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			s.mu.Unlock() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		case <-s.die: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			if s.l != nil { // has listener 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				s.l.chDeadlinks <- s.remote 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			return 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-// GetConv gets conversation id of a session 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-func (s *UDPSession) GetConv() uint32 { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	return s.kcp.conv 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-func (s *UDPSession) notifyReadEvent() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	select { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	case s.chReadEvent <- struct{}{}: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	default: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-func (s *UDPSession) notifyWriteEvent() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	select { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	case s.chWriteEvent <- struct{}{}: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	default: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-func (s *UDPSession) kcpInput(data []byte) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	now := time.Now() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	if now.Sub(s.lastInputTs) > connTimeout { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		s.Close() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		return 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	s.lastInputTs = now 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	s.mu.Lock() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	s.kcp.current = currentMs() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	s.kcp.Input(data) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	if s.ackNoDelay { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		s.kcp.current = currentMs() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		s.kcp.flush() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	} else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		s.needUpdate = true 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	s.mu.Unlock() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	s.notifyReadEvent() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-func (s *UDPSession) receiver(ch chan []byte) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	for { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		data := make([]byte, mtuLimit) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		if n, _, err := s.conn.ReadFromUDP(data); err == nil && n >= s.headerSize+IKCP_OVERHEAD { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			ch <- data[:n] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		} else if err != nil { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			return 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-// read loop for client session 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-func (s *UDPSession) readLoop() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	chPacket := make(chan []byte, rxQueueLimit) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	go s.receiver(chPacket) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	for { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		select { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		case data := <-chPacket: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			dataValid := false 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			if s.block != nil { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				s.block.Decrypt(data, data) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				data = data[otpSize:] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				checksum := crc32.ChecksumIEEE(data[crcSize:]) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				if checksum == binary.LittleEndian.Uint32(data) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					data = data[crcSize:] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					dataValid = true 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			} else if s.block == nil { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				dataValid = true 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			if dataValid { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				s.kcpInput(data) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		case <-s.die: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			return 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-type ( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	// Listener defines a server listening for connections 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	Listener struct { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		block       BlockCrypt 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		conn        *net.UDPConn 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		sessions    map[string]*UDPSession 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		chAccepts   chan *UDPSession 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		chDeadlinks chan net.Addr 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		headerSize  int 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		die         chan struct{} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	packet struct { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		from *net.UDPAddr 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		data []byte 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-// monitor incoming data for all connections of server 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-func (l *Listener) monitor() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	chPacket := make(chan packet, rxQueueLimit) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	go l.receiver(chPacket) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	ticker := time.NewTicker(10 * time.Millisecond) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	defer ticker.Stop() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	for { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		select { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		case p := <-chPacket: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			data := p.data 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			from := p.from 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			dataValid := false 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			if l.block != nil { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				l.block.Decrypt(data, data) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				data = data[otpSize:] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				checksum := crc32.ChecksumIEEE(data[crcSize:]) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				if checksum == binary.LittleEndian.Uint32(data) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					data = data[crcSize:] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					dataValid = true 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			} else if l.block == nil { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				dataValid = true 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			if dataValid { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				addr := from.String() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				s, ok := l.sessions[addr] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				if !ok { // new session 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					var conv uint32 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					convValid := false 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					conv = binary.LittleEndian.Uint32(data) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					convValid = true 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					if convValid { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-						s := newUDPSession(conv, l, l.conn, from, l.block) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-						s.kcpInput(data) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-						l.sessions[addr] = s 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-						l.chAccepts <- s 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				} else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-					s.kcpInput(data) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		case deadlink := <-l.chDeadlinks: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			delete(l.sessions, deadlink.String()) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		case <-l.die: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			return 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		case <-ticker.C: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			now := time.Now() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			for _, s := range l.sessions { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				select { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				case s.chTicker <- now: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				default: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-				} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-func (l *Listener) receiver(ch chan packet) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	for { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		data := make([]byte, mtuLimit) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		if n, from, err := l.conn.ReadFromUDP(data); err == nil && n >= l.headerSize+IKCP_OVERHEAD { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			ch <- packet{from, data[:n]} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		} else if err != nil { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-			return 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-// Accept implements the Accept method in the Listener interface; it waits for the next call and returns a generic Conn. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-func (l *Listener) Accept() (*UDPSession, error) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	select { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	case c := <-l.chAccepts: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		return c, nil 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	case <-l.die: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		return nil, errors.New("listener stopped") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-// Close stops listening on the UDP address. Already Accepted connections are not closed. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-func (l *Listener) Close() error { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	if err := l.conn.Close(); err == nil { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		close(l.die) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		return nil 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	} else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-		return err 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-// Addr returns the listener's network address, The Addr returned is shared by all invocations of Addr, so do not modify it. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-func (l *Listener) Addr() net.Addr { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	return l.conn.LocalAddr() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-func currentMs() uint32 { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-	return uint32(time.Now().UnixNano() / int64(time.Millisecond)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-} 
			 |