func newGettyWSConn(conn *websocket.Conn) *gettyWSConn {
	if conn == nil {
		panic("newGettyWSConn(conn):@conn is nil")
	}
	var localAddr, peerAddr string
	if conn.LocalAddr() != nil {
		localAddr = conn.LocalAddr().String()
	}
	if conn.RemoteAddr() != nil {
		peerAddr = conn.RemoteAddr().String()
	}
	gettyWSConn := &gettyWSConn{
		conn: conn,
		gettyConn: gettyConn{
			id:       atomic.AddUint32(&connID, 1),
			rTimeout: netIOTimeout,
			wTimeout: netIOTimeout,
			local:    localAddr,
			peer:     peerAddr,
			compress: CompressNone,
		},
	}
	conn.EnableWriteCompression(false)
	conn.SetPingHandler(gettyWSConn.handlePing)
	conn.SetPongHandler(gettyWSConn.handlePong)
	return gettyWSConn
}
create websocket connection
package getty
import (
	"compress/flate"
	"crypto/tls"
	"fmt"
	"io"
	"net"
	"sync"
	"sync/atomic"
	"time"
)
import (
	"github.com/golang/snappy"
	"github.com/gorilla/websocket"
	perrors "github.com/pkg/errors"
)
var (
	launchTime = time.Now()
)
var (
	connID uint32
)
type gettyConn struct {
	id            uint32
	compress      CompressType
	padding1      uint8
	padding2      uint16
	readBytes     uint32         
	writeBytes    uint32         
	readPkgNum    uint32         
	writePkgNum   uint32         
	active        int64          
	rTimeout      time.Duration  
	wTimeout      time.Duration
	rLastDeadline time.Time  
	wLastDeadline time.Time  
	local         string     
	peer          string     
	ss            Session
}
func (c *gettyConn) ID() uint32 {
	return c.id
}
func (c *gettyConn) LocalAddr() string {
	return c.local
}
func (c *gettyConn) RemoteAddr() string {
	return c.peer
}
func (c *gettyConn) incReadPkgNum() {
	atomic.AddUint32(&c.readPkgNum, 1)
}
func (c *gettyConn) incWritePkgNum() {
	atomic.AddUint32(&c.writePkgNum, 1)
}
func (c *gettyConn) UpdateActive() {
	atomic.StoreInt64(&(c.active), int64(time.Since(launchTime)))
}
func (c *gettyConn) GetActive() time.Time {
	return launchTime.Add(time.Duration(atomic.LoadInt64(&(c.active))))
}
func (c *gettyConn) send(interface{}) (int, error) {
	return 0, nil
}
func (c *gettyConn) close(int) {}
func (c gettyConn) readTimeout() time.Duration {
	return c.rTimeout
}
func (c *gettyConn) setSession(ss Session) {
	c.ss = ss
}
func (c *gettyConn) SetReadTimeout(rTimeout time.Duration) {
	if rTimeout < 1 {
		panic("@rTimeout < 1")
	}
	c.rTimeout = rTimeout
	if c.wTimeout == 0 {
		c.wTimeout = rTimeout
	}
}
func (c gettyConn) writeTimeout() time.Duration {
	return c.wTimeout
}
func (c *gettyConn) SetWriteTimeout(wTimeout time.Duration) {
	if wTimeout < 1 {
		panic("@wTimeout < 1")
	}
	c.wTimeout = wTimeout
	if c.rTimeout == 0 {
		c.rTimeout = wTimeout
	}
}
type gettyTCPConn struct {
	gettyConn
	reader io.Reader
	writer io.Writer
	conn   net.Conn
}
func newGettyTCPConn(conn net.Conn) *gettyTCPConn {
	if conn == nil {
		panic("newGettyTCPConn(conn):@conn is nil")
	}
	var localAddr, peerAddr string
	if conn.LocalAddr() != nil {
		localAddr = conn.LocalAddr().String()
	}
	if conn.RemoteAddr() != nil {
		peerAddr = conn.RemoteAddr().String()
	}
	return &gettyTCPConn{
		conn:   conn,
		reader: io.Reader(conn),
		writer: io.Writer(conn),
		gettyConn: gettyConn{
			id:       atomic.AddUint32(&connID, 1),
			rTimeout: netIOTimeout,
			wTimeout: netIOTimeout,
			local:    localAddr,
			peer:     peerAddr,
			compress: CompressNone,
		},
	}
}
type writeFlusher struct {
	flusher *flate.Writer
	lock    sync.Mutex
}
func (t *writeFlusher) Write(p []byte) (int, error) {
	var (
		n   int
		err error
	)
	t.lock.Lock()
	defer t.lock.Unlock()
	n, err = t.flusher.Write(p)
	if err != nil {
		return n, perrors.WithStack(err)
	}
	if err := t.flusher.Flush(); err != nil {
		return 0, perrors.WithStack(err)
	}
	return n, nil
}
func (t *gettyTCPConn) SetCompressType(c CompressType) {
	switch c {
	case CompressNone, CompressZip, CompressBestSpeed, CompressBestCompression, CompressHuffman:
		ioReader := io.Reader(t.conn)
		t.reader = flate.NewReader(ioReader)
		ioWriter := io.Writer(t.conn)
		w, err := flate.NewWriter(ioWriter, int(c))
		if err != nil {
			panic(fmt.Sprintf("flate.NewReader(flate.DefaultCompress) = err(%s)", err))
		}
		t.writer = &writeFlusher{flusher: w}
	case CompressSnappy:
		ioReader := io.Reader(t.conn)
		t.reader = snappy.NewReader(ioReader)
		ioWriter := io.Writer(t.conn)
		t.writer = snappy.NewBufferedWriter(ioWriter)
	default:
		panic(fmt.Sprintf("illegal comparess type %d", c))
	}
	t.compress = c
}
func (t *gettyTCPConn) recv(p []byte) (int, error) {
	var (
		err         error
		currentTime time.Time
		length      int
	)
	if t.compress == CompressNone && t.rTimeout > 0 {
		currentTime = time.Now()
		if currentTime.Sub(t.rLastDeadline) > (t.rTimeout >> 2) {
			if err = t.conn.SetReadDeadline(currentTime.Add(t.rTimeout)); err != nil {
				return 0, perrors.WithStack(err)
			}
			t.rLastDeadline = currentTime
		}
	}
	length, err = t.reader.Read(p)
	atomic.AddUint32(&t.readBytes, uint32(length))
	return length, perrors.WithStack(err)
}
func (t *gettyTCPConn) send(pkg interface{}) (int, error) {
	var (
		err         error
		currentTime time.Time
		ok          bool
		p           []byte
		length      int
	)
	if t.compress == CompressNone && t.wTimeout > 0 {
		currentTime = time.Now()
		if currentTime.Sub(t.wLastDeadline) > (t.wTimeout >> 2) {
			if err = t.conn.SetWriteDeadline(currentTime.Add(t.wTimeout)); err != nil {
				return 0, perrors.WithStack(err)
			}
			t.wLastDeadline = currentTime
		}
	}
	if buffers, ok := pkg.([][]byte); ok {
		netBuf := net.Buffers(buffers)
		if length, err := netBuf.WriteTo(t.conn); err == nil {
			atomic.AddUint32(&t.writeBytes, (uint32)(length))
			atomic.AddUint32(&t.writePkgNum, (uint32)(len(buffers)))
		}
		log.Debugf("localAddr: %s, remoteAddr:%s, now:%s, length:%d, err:%s",
			t.conn.LocalAddr(), t.conn.RemoteAddr(), currentTime, length, err)
		return int(length), perrors.WithStack(err)
	}
	if p, ok = pkg.([]byte); ok {
		if length, err = t.writer.Write(p); err == nil {
			atomic.AddUint32(&t.writeBytes, (uint32)(len(p)))
			atomic.AddUint32(&t.writePkgNum, 1)
		}
		log.Debugf("localAddr: %s, remoteAddr:%s, now:%s, length:%d, err:%s",
			t.conn.LocalAddr(), t.conn.RemoteAddr(), currentTime, length, err)
		return length, perrors.WithStack(err)
	}
	return 0, perrors.Errorf("illegal @pkg{%#v} type", pkg)
}
func (t *gettyTCPConn) close(waitSec int) {
	if t.conn != nil {
		if writer, ok := t.writer.(*snappy.Writer); ok {
			if err := writer.Close(); err != nil {
				log.Errorf("snappy.Writer.Close() = error:%+v", err)
			}
		}
		t.conn.(*net.TCPConn).SetLinger(waitSec)
		t.conn.Close()
		t.conn = nil
	}
}
type UDPContext struct {
	Pkg      interface{}
	PeerAddr *net.UDPAddr
}
func (c UDPContext) String() string {
	return fmt.Sprintf("{pkg:%#v, peer addr:%s}", c.Pkg, c.PeerAddr)
}
type gettyUDPConn struct {
	gettyConn
	compressType CompressType
	conn         *net.UDPConn  
}
func newGettyUDPConn(conn *net.UDPConn) *gettyUDPConn {
	if conn == nil {
		panic("newGettyUDPConn(conn):@conn is nil")
	}
	var localAddr, peerAddr string
	if conn.LocalAddr() != nil {
		localAddr = conn.LocalAddr().String()
	}
	if conn.RemoteAddr() != nil {
		peerAddr = conn.RemoteAddr().String()
	}
	return &gettyUDPConn{
		conn: conn,
		gettyConn: gettyConn{
			id:       atomic.AddUint32(&connID, 1),
			rTimeout: netIOTimeout,
			wTimeout: netIOTimeout,
			local:    localAddr,
			peer:     peerAddr,
			compress: CompressNone,
		},
	}
}
func (u *gettyUDPConn) SetCompressType(c CompressType) {
	switch c {
	case CompressNone, CompressZip, CompressBestSpeed, CompressBestCompression, CompressHuffman, CompressSnappy:
		u.compressType = c
	default:
		panic(fmt.Sprintf("illegal comparess type %d", c))
	}
}
func (u *gettyUDPConn) recv(p []byte) (int, *net.UDPAddr, error) {
	var (
		err         error
		currentTime time.Time
		length      int
		addr        *net.UDPAddr
	)
	if u.rTimeout > 0 {
		currentTime = time.Now()
		if currentTime.Sub(u.rLastDeadline) > (u.rTimeout >> 2) {
			if err = u.conn.SetReadDeadline(currentTime.Add(u.rTimeout)); err != nil {
				return 0, nil, perrors.WithStack(err)
			}
			u.rLastDeadline = currentTime
		}
	}
	length, addr, err = u.conn.ReadFromUDP(p)  
	log.Debugf("ReadFromUDP() = {length:%d, peerAddr:%s, error:%v}", length, addr, err)
	if err == nil {
		atomic.AddUint32(&u.readBytes, uint32(length))
	}
	return length, addr, perrors.WithStack(err)
}
func (u *gettyUDPConn) send(udpCtx interface{}) (int, error) {
	var (
		err         error
		currentTime time.Time
		length      int
		ok          bool
		ctx         UDPContext
		buf         []byte
		peerAddr    *net.UDPAddr
	)
	if ctx, ok = udpCtx.(UDPContext); !ok {
		return 0, perrors.Errorf("illegal @udpCtx{%s} type, @udpCtx type:%T", udpCtx, udpCtx)
	}
	if buf, ok = ctx.Pkg.([]byte); !ok {
		return 0, perrors.Errorf("illegal @udpCtx.Pkg{%#v} type", udpCtx)
	}
	if u.ss.EndPoint().EndPointType() == UDP_ENDPOINT {
		peerAddr = ctx.PeerAddr
		if peerAddr == nil {
			return 0, ErrNullPeerAddr
		}
	}
	if u.wTimeout > 0 {
		currentTime = time.Now()
		if currentTime.Sub(u.wLastDeadline) > (u.wTimeout >> 2) {
			if err = u.conn.SetWriteDeadline(currentTime.Add(u.wTimeout)); err != nil {
				return 0, perrors.WithStack(err)
			}
			u.wLastDeadline = currentTime
		}
	}
	if length, _, err = u.conn.WriteMsgUDP(buf, nil, peerAddr); err == nil {
		atomic.AddUint32(&u.writeBytes, (uint32)(len(buf)))
		atomic.AddUint32(&u.writePkgNum, 1)
	}
	log.Debugf("WriteMsgUDP(peerAddr:%s) = {length:%d, error:%v}", peerAddr, length, err)
	return length, perrors.WithStack(err)
}
func (u *gettyUDPConn) close(_ int) {
	if u.conn != nil {
		u.conn.Close()
		u.conn = nil
	}
}
type gettyWSConn struct {
	gettyConn
	conn *websocket.Conn
}