func ClientWithContext(ctx context.Context, conn net.Conn, config *Config) (*Conn, error) {
	switch {
	case config == nil:
		return nil, errNoConfigProvided
	case config.PSK != nil && config.PSKIdentityHint == nil:
		return nil, errPSKAndIdentityMustBeSetForClient
	}
	return createConn(ctx, conn, config, true, nil)
}
ClientWithContext establishes a DTLS connection over an existing connection.
package dtls
import (
	"context"
	"errors"
	"fmt"
	"io"
	"net"
	"sync"
	"sync/atomic"
	"time"
	"github.com/pion/dtls/v2/internal/closer"
	"github.com/pion/dtls/v2/pkg/crypto/elliptic"
	"github.com/pion/dtls/v2/pkg/crypto/signaturehash"
	"github.com/pion/dtls/v2/pkg/protocol"
	"github.com/pion/dtls/v2/pkg/protocol/alert"
	"github.com/pion/dtls/v2/pkg/protocol/handshake"
	"github.com/pion/dtls/v2/pkg/protocol/recordlayer"
	"github.com/pion/logging"
	"github.com/pion/transport/connctx"
	"github.com/pion/transport/deadline"
	"github.com/pion/transport/replaydetector"
)
const (
	initialTickerInterval = time.Second
	cookieLength          = 20
	defaultNamedCurve     = elliptic.X25519
	inboundBufferSize     = 8192
	defaultReplayProtectionWindow = 64
)
func invalidKeyingLabels() map[string]bool {
	return map[string]bool{
		"client finished": true,
		"server finished": true,
		"master secret":   true,
		"key expansion":   true,
	}
}
type Conn struct {
	lock           sync.RWMutex      
	nextConn       connctx.ConnCtx   
	fragmentBuffer *fragmentBuffer   
	handshakeCache *handshakeCache   
	decrypted      chan interface{}  
	state State  
	maximumTransmissionUnit int
	handshakeCompletedSuccessfully atomic.Value
	encryptedPackets [][]byte
	connectionClosedByUser bool
	closeLock              sync.Mutex
	closed                 *closer.Closer
	handshakeLoopsFinished sync.WaitGroup
	readDeadline  *deadline.Deadline
	writeDeadline *deadline.Deadline
	log logging.LeveledLogger
	reading               chan struct{}
	handshakeRecv         chan chan struct{}
	cancelHandshaker      func()
	cancelHandshakeReader func()
	fsm *handshakeFSM
	replayProtectionWindow uint
}
func createConn(ctx context.Context, nextConn net.Conn, config *Config, isClient bool, initialState *State) (*Conn, error) {
	err := validateConfig(config)
	if err != nil {
		return nil, err
	}
	if nextConn == nil {
		return nil, errNilNextConn
	}
	cipherSuites, err := parseCipherSuites(config.CipherSuites, config.CustomCipherSuites, config.PSK == nil || len(config.Certificates) > 0, config.PSK != nil)
	if err != nil {
		return nil, err
	}
	signatureSchemes, err := signaturehash.ParseSignatureSchemes(config.SignatureSchemes, config.InsecureHashes)
	if err != nil {
		return nil, err
	}
	workerInterval := initialTickerInterval
	if config.FlightInterval != 0 {
		workerInterval = config.FlightInterval
	}
	loggerFactory := config.LoggerFactory
	if loggerFactory == nil {
		loggerFactory = logging.NewDefaultLoggerFactory()
	}
	logger := loggerFactory.NewLogger("dtls")
	mtu := config.MTU
	if mtu <= 0 {
		mtu = defaultMTU
	}
	replayProtectionWindow := config.ReplayProtectionWindow
	if replayProtectionWindow <= 0 {
		replayProtectionWindow = defaultReplayProtectionWindow
	}
	c := &Conn{
		nextConn:                connctx.New(nextConn),
		fragmentBuffer:          newFragmentBuffer(),
		handshakeCache:          newHandshakeCache(),
		maximumTransmissionUnit: mtu,
		decrypted: make(chan interface{}, 1),
		log:       logger,
		readDeadline:  deadline.New(),
		writeDeadline: deadline.New(),
		reading:          make(chan struct{}, 1),
		handshakeRecv:    make(chan chan struct{}),
		closed:           closer.NewCloser(),
		cancelHandshaker: func() {},
		replayProtectionWindow: uint(replayProtectionWindow),
		state: State{
			isClient: isClient,
		},
	}
	c.setRemoteEpoch(0)
	c.setLocalEpoch(0)
	serverName := config.ServerName
	if isClient && serverName == "" && nextConn.RemoteAddr() != nil {
		remoteAddr := nextConn.RemoteAddr().String()
		var host string
		host, _, err = net.SplitHostPort(remoteAddr)
		if err != nil {
			serverName = remoteAddr
		} else {
			serverName = host
		}
	}
	hsCfg := &handshakeConfig{
		localPSKCallback:            config.PSK,
		localPSKIdentityHint:        config.PSKIdentityHint,
		localCipherSuites:           cipherSuites,
		localSignatureSchemes:       signatureSchemes,
		extendedMasterSecret:        config.ExtendedMasterSecret,
		localSRTPProtectionProfiles: config.SRTPProtectionProfiles,
		serverName:                  serverName,
		clientAuth:                  config.ClientAuth,
		localCertificates:           config.Certificates,
		insecureSkipVerify:          config.InsecureSkipVerify,
		verifyPeerCertificate:       config.VerifyPeerCertificate,
		rootCAs:                     config.RootCAs,
		clientCAs:                   config.ClientCAs,
		customCipherSuites:          config.CustomCipherSuites,
		retransmitInterval:          workerInterval,
		log:                         logger,
		initialEpoch:                0,
		keyLogWriter:                config.KeyLogWriter,
	}
	var initialFlight flightVal
	var initialFSMState handshakeState
	if initialState != nil {
		if c.state.isClient {
			initialFlight = flight5
		} else {
			initialFlight = flight6
		}
		initialFSMState = handshakeFinished
		c.state = *initialState
	} else {
		if c.state.isClient {
			initialFlight = flight1
		} else {
			initialFlight = flight0
		}
		initialFSMState = handshakePreparing
	}
	if err := c.handshake(ctx, hsCfg, initialFlight, initialFSMState); err != nil {
		return nil, err
	}
	c.log.Trace("Handshake Completed")
	return c, nil
}
func Dial(network string, raddr *net.UDPAddr, config *Config) (*Conn, error) {
	ctx, cancel := config.connectContextMaker()
	defer cancel()
	return DialWithContext(ctx, network, raddr, config)
}
func Client(conn net.Conn, config *Config) (*Conn, error) {
	ctx, cancel := config.connectContextMaker()
	defer cancel()
	return ClientWithContext(ctx, conn, config)
}
func Server(conn net.Conn, config *Config) (*Conn, error) {
	ctx, cancel := config.connectContextMaker()
	defer cancel()
	return ServerWithContext(ctx, conn, config)
}
func DialWithContext(ctx context.Context, network string, raddr *net.UDPAddr, config *Config) (*Conn, error) {
	pConn, err := net.DialUDP(network, nil, raddr)
	if err != nil {
		return nil, err
	}
	return ClientWithContext(ctx, pConn, config)
}