func (s *Conn) CloseWithError(code uint64, reason string) error {
	return s.conn.Close(code, reason, true)
}
CloseWithError sets the connection to closing state with an
application code code and reason sending to peer.
The function returns error if peer has already initiated closing.
package quic
import (
	"crypto/rand"
	"io"
	"net"
	"sync"
	"time"
	"github.com/goburrow/quic/transport"
)
const (
	cidLength = 16  
	bufferSize = 1500
	keyUnavailableTrigger = "key_unavailable"
)
type Handler interface {
	Serve(conn *Conn, events []transport.Event)
}
type noopHandler struct{}
func (s noopHandler) Serve(*Conn, []transport.Event) {}
type Conn struct {
	conn    *transport.Conn
	addr    net.Addr
	udpAddr net.UDPAddr
	scid    []byte
	streams  map[uint64]*Stream  
	datagram *Datagram           
	events []transport.Event
	recvCh chan *packet
	cmdCh  chan connCommand
	timeoutTimer *time.Timer  
	attemptKey []byte
	nextStreamIDBidi uint64
	nextStreamIDUni  uint64
	queuedPackets *packet
	userData interface{}
}
func newRemoteConn(addr net.Addr, scid []byte, conn *transport.Conn, isClient bool) *Conn {
	c := &Conn{
		scid: scid,
		conn: conn,
		streams: make(map[uint64]*Stream),
		recvCh:  make(chan *packet, 16),
		cmdCh:   make(chan connCommand, 8),
		nextStreamIDBidi: 0,  
		nextStreamIDUni:  2,  
	}
	if udpAddr, ok := addr.(*net.UDPAddr); ok {
		c.udpAddr = *udpAddr
		c.addr = &c.udpAddr
	} else {
		c.addr = addr
	}
	if !isClient {
		c.nextStreamIDBidi++
		c.nextStreamIDUni++
	}
	return c
}
func (s *Conn) ConnectionState() transport.ConnectionState {
	return s.conn.ConnectionState()
}
func (s *Conn) StreamWrite(streamID uint64, b []byte) (int, error) {
	st, err := s.conn.Stream(streamID)
	if err != nil {
		return 0, err
	}
	return st.Write(b)
}
func (s *Conn) StreamRead(streamID uint64, b []byte) (int, error) {
	st, err := s.conn.Stream(streamID)
	if err != nil {
		return 0, err
	}
	return st.Read(b)
}
func (s *Conn) StreamWriteTo(streamID uint64, w io.Writer) (int64, error) {
	st, err := s.conn.Stream(streamID)
	if err != nil {
		return 0, err
	}
	return st.WriteTo(w)
}
func (s *Conn) StreamCloseWrite(streamID uint64, errorCode uint64) error {
	st, err := s.conn.Stream(streamID)
	if err != nil {
		return err
	}
	return st.CloseWrite(errorCode)
}
func (s *Conn) StreamCloseRead(streamID uint64, errorCode uint64) error {
	st, err := s.conn.Stream(streamID)
	if err != nil {
		return err
	}
	return st.CloseRead(errorCode)
}
func (s *Conn) StreamClose(streamID uint64) error {
	st, err := s.conn.Stream(streamID)
	if err != nil {
		return err
	}
	return st.Close()
}
func (s *Conn) Stream(streamID uint64) (*Stream, error) {
	if st := s.streams[streamID]; st != nil {
		return st, nil
	}
	_, err := s.conn.Stream(streamID)
	if err != nil {
		return nil, err
	}
	st := newStream(s, streamID)
	s.streams[streamID] = st
	return st, nil
}
func (s *Conn) NewStream(bidi bool) (uint64, bool) {
	var id uint64
	if bidi {
		id = s.nextStreamIDBidi
	} else {
		id = s.nextStreamIDUni
	}
	_, err := s.conn.Stream(id)
	if err != nil {
		return 0, false
	}
	if bidi {
		s.nextStreamIDBidi += 4
	} else {
		s.nextStreamIDUni += 4
	}
	return id, true
}
func (s *Conn) DatagramWrite(b []byte) (int, error) {
	return s.conn.Datagram().Write(b)
}
func (s *Conn) DatagramRead(b []byte) (int, error) {
	return s.conn.Datagram().Read(b)
}
func (s *Conn) Datagram() *Datagram {
	if s.datagram == nil {
		s.datagram = newDatagram(s)
	}
	return s.datagram
}
func (s *Conn) LocalAddr() net.Addr {
	return s.addr  
}
func (s *Conn) RemoteAddr() net.Addr {
	return s.addr
}
func (s *Conn) SetUserData(data interface{}) {
	s.userData = data
}
func (s *Conn) UserData() interface{} {
	return s.userData
}
func (s *Conn) Close() error {
	return s.conn.Close(transport.NoError, "bye", false)
}