Inheritance: IDisposable
        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="socket"></param>
        /// <param name="targetHost"></param>
        /// <param name="autoHandle"></param>
        public RdpemtClient(RdpeudpSocket socket, string targetHost, bool autoHandle = true)
            : base(autoHandle)
        {
            if (!socket.AutoHandle)
            {
                throw new NotSupportedException("To Create RDPEMT Server, RDPEUDP Socket must be auto handle.");
            }

            if (socket.TransMode == TransportMode.Reliable)
            {
                RdpeudpTLSChannel secChannel = new RdpeudpTLSChannel(socket);
                secChannel.Received += ReceiveBytes;
                secChannel.AuthenticateAsClient(targetHost);
                this.secureChannel = secChannel;
            }
            else
            {
                RdpeudpDTLSChannel secChannel = new RdpeudpDTLSChannel(socket);
                secChannel.Received += ReceiveBytes;
                secChannel.AuthenticateAsClient(targetHost);
                this.secureChannel = secChannel;
            }

            bandwidthMeasureStartTime = DateTime.Now;
            bandwidthMeasurePayloadByteCount = 0;
            detectBandwidth = false;
        }
        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="eudpSocket">RDPEUDP Socket, which is transport</param>
        public RdpeudpDTLSChannel(RdpeudpSocket eudpSocket)
        {
            if (eudpSocket == null || eudpSocket.TransMode == TransportMode.Reliable)
            {
                throw new NotSupportedException("RdpeudpSocket is null or it is a socket for reliable RDPEUDP connection.");
            }

            this.rdpeudpSocket = eudpSocket;
            this.rdpeudpSocket.Received += ReceiveBytes;
            isAuthenticated = false;

            receivedBuffer = new List<byte[]>();
            toSendBuffer = new List<byte[]>();

            if(eudpSocket.AutoHandle)
            {
                // Check whether there is packets in unprocessed packet buffer

                RdpeudpPacket packet = eudpSocket.ExpectPacket(shortWaitTime);
                if (packet != null)
                {
                    eudpSocket.ReceivePacket(packet);
                }
            }
        }
        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="eudpSocket">A RdpeudpSocket used for transport</param>
        public RdpeudpTLSChannel(RdpeudpSocket eudpSocket)
        {
            if (eudpSocket == null || eudpSocket.TransMode == TransportMode.Lossy)
            {
                throw new NotSupportedException("RdpeudpSocket is null or it is a socket for Lossy RDPEUDP connection.");
            }
            this.rdpeudpSocket = eudpSocket;
            this.rdpeudpSocket.Received += ReceiveBytes;
            innerStream = new SSLInnerStream();
            isAuthenticated = false;

            if (eudpSocket.AutoHandle)
            {
                // Check whether there is packets in unprocessed packet buffer

                RdpeudpPacket packet = eudpSocket.ExpectPacket(shortWaitTime);
                if (packet != null)
                {
                    eudpSocket.ReceivePacket(packet);
                }
            }
        }
        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="socket"></param>
        /// <param name="cert"></param>
        /// <param name="autoHandle"></param>
        public RdpemtServer(RdpeudpSocket socket, X509Certificate2 cert, bool autoHandle = true)
            : base(autoHandle)
        {
            if (!socket.AutoHandle)
            {
                throw new NotSupportedException("To Create RDPEMT Server, RDPEUDP Socket must be auto handle.");
            }

            if (socket.TransMode == TransportMode.Reliable)
            {
                RdpeudpTLSChannel secChannel = new RdpeudpTLSChannel(socket);
                secChannel.Received += ReceiveBytes;
                secChannel.AuthenticateAsServer(cert);
                this.secureChannel = secChannel;
            }
            else
            {
                RdpeudpDTLSChannel secChannel = new RdpeudpDTLSChannel(socket);
                secChannel.Received += ReceiveBytes;
                secChannel.AuthenticateAsServer(cert);
                this.secureChannel = secChannel;
            }
        }
        /// <summary>
        /// Establish a UDP connection
        /// </summary>
        /// <param name="udpTransportMode">Transport mode: Reliable or Lossy</param>
        /// <param name="timeout">wait time</param>
        /// <returns>The accepted socket</returns>
        private void EstablishUDPConnection(TransportMode udpTransportMode, TimeSpan timeout)
        {
            //Start UDP listening
            if (rdpeudpServer == null)
                rdpeudpServer = new RdpeudpServer((IPEndPoint)this.rdpbcgrAdapter.SessionContext.LocalIdentity, true);
            if (!rdpeudpServer.Running)
                rdpeudpServer.Start();

            //Send a Server Initiate Multitransport Request PDU
            byte[] securityCookie = new byte[16];
            Random rnd = new Random();
            rnd.NextBytes(securityCookie);
            Multitransport_Protocol_value requestedProtocol = Multitransport_Protocol_value.INITITATE_REQUEST_PROTOCOL_UDPFECR;
            if (udpTransportMode == TransportMode.Lossy)
            {
                requestedProtocol = Multitransport_Protocol_value.INITITATE_REQUEST_PROTOCOL_UDPFECL;
            }
            this.rdpbcgrAdapter.SendServerInitiateMultitransportRequestPDU(++this.multitransportRequestId, requestedProtocol, securityCookie);
            this.requestIdList.Add(this.multitransportRequestId);
            this.securityCookieList.Add(securityCookie);

            //create a UDP socket
            RdpeudpSocket rdpudpSocket = rdpeudpServer.Accept(((IPEndPoint)this.rdpbcgrAdapter.SessionContext.Identity).Address, udpTransportMode, timeout);

            if (udpTransportMode == TransportMode.Reliable)
            {
                this.rdpeudpSocketR = rdpudpSocket;
            }
            else
            {
                this.rdpeudpSocketL = rdpudpSocket;
            }
        }
        /// <summary>
        /// Establish a MultiTransport Connection
        /// </summary>
        private void EstablishTransportConnection()
        {
            // Send the Server Initial multitransport
            byte[] securityCookie = new byte[16];
            Random rnd = new Random();
            rnd.NextBytes(securityCookie);

            Server_Initiate_Multitransport_Request_PDU requestPDU = rdpbcgrServer.CreateServerInitiateMultitransportRequestPDU(serverSessionContext, ++multitransportId, transportProtocol, securityCookie);
            rdpbcgrServer.SendPdu(serverSessionContext, requestPDU);

            //Create RDP-UDP Connection
            CreateRdpeudpServer(this.serverSessionContext);
            TransportMode transMode = TransportMode.Reliable;
            if (transportProtocol == Multitransport_Protocol_value.INITITATE_REQUEST_PROTOCOL_UDPFECL)
            {
                transMode = TransportMode.Lossy;
            }

            rdpeudpSocket = rdpeudpServer.Accept(((IPEndPoint)serverSessionContext.Identity).Address, transMode, timeout);
            if(rdpeudpSocket == null)
            {
                if (rdpeudpServer != null && rdpeudpServer.Running)
                    rdpeudpServer.Dispose();

                throw new NotSupportedException("RDPEMT Server create rdpedupSocket failed.");
            }
            rdpemtServer = new RdpemtServer(rdpeudpSocket, rdpbcgrServer.AuthCertificate, true);
            rdpemtServer.Received += ReceivedBytes;

            uint receivedRequestId;
            byte[] receivedCookie;
            if (!rdpemtServer.ExpectConnect(timeout, out receivedRequestId, out receivedCookie))
            {
                throw new ProtocolViolationException("RDPEMT Server Expect Connection failed");
            }
            if (receivedRequestId != multitransportId || receivedCookie == null || receivedCookie.Length != 16)
            {
                throw new ProtocolViolationException("RDPEMT Server received a connection with un-expected request id or Cookie is null (or cookie's length is not 16)!");
            }

            for (int i = 0; i < receivedCookie.Length; i++)
            {
                if (receivedCookie[i] != securityCookie[i])
                {
                    throw new ProtocolViolationException("RDPEMT Server received a connection with un-correct cookie!");
                }
            }
        }