|  | @@ -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))
 |  | 
 | 
											
												
													
														|  | -}
 |  | 
 |