/// <summary>
        /// Event handler for a packet receive on the UDP encapsulation socket.
        /// </summary>
        /// <param name="receiver">The UDP receiver that received the packet.</param>
        /// <param name="localPort">The local port the packet was received on.</param>
        /// <param name="remoteEndPoint">The remote end point the packet was received from.</param>
        /// <param name="packet">A buffer containing the packet.</param>
        private void OnEncapsulationSocketPacketReceived(UdpReceiver receiver, int localPort, IPEndPoint remoteEndPoint, byte[] packet)
        {
            try
            {
                if (!SctpPacket.VerifyChecksum(packet, 0, packet.Length))
                {
                    logger.LogWarning($"SCTP packet from UDP {remoteEndPoint} dropped due to invalid checksum.");
                }
                else
                {
                    var sctpPacket = SctpPacket.Parse(packet, 0, packet.Length);

                    // Process packet.
                    if (sctpPacket.Header.VerificationTag == 0)
                    {
                        GotInit(sctpPacket, remoteEndPoint);
                    }
                    else if (sctpPacket.Chunks.Any(x => x.KnownType == SctpChunkType.COOKIE_ECHO))
                    {
                        // The COOKIE ECHO chunk is the 3rd step in the SCTP handshake when the remote party has
                        // requested a new association be created.
                        var cookie = base.GetCookie(sctpPacket);

                        if (cookie.IsEmpty())
                        {
                            logger.LogWarning($"SCTP error acquiring handshake cookie from COOKIE ECHO chunk.");
                        }
                        else
                        {
                            logger.LogDebug($"SCTP creating new association for {remoteEndPoint}.");

                            var association = new SctpAssociation(this, cookie, localPort);

                            if (_associations.TryAdd(association.ID, association))
                            {
                                if (sctpPacket.Chunks.Count > 1)
                                {
                                    // There could be DATA chunks after the COOKIE ECHO chunk.
                                    association.OnPacketReceived(sctpPacket);
                                }
                            }
                            else
                            {
                                logger.LogError($"SCTP failed to add new association to dictionary.");
                            }
                        }
                    }
                    else
                    {
                        // TODO: Lookup the existing association for the packet.
                        _associations.Values.First().OnPacketReceived(sctpPacket);
                    }
                }
            }
            catch (Exception excp)
            {
                logger.LogError($"Exception SctpTransport.OnEncapsulationSocketPacketReceived. {excp}");
            }
        }
        /// <summary>
        /// This method runs on a dedicated thread to listen for incoming SCTP
        /// packets on the DTLS transport.
        /// </summary>
        private void DoReceive(object state)
        {
            byte[] recvBuffer = new byte[SctpAssociation.DEFAULT_ADVERTISED_RECEIVE_WINDOW];

            while (!_isClosed)
            {
                try
                {
                    int bytesRead = transport.Receive(recvBuffer, 0, recvBuffer.Length, RECEIVE_TIMEOUT_MILLISECONDS);

                    if (bytesRead == DtlsSrtpTransport.DTLS_RETRANSMISSION_CODE)
                    {
                        // Timed out waiting for a packet, this is by design and the receive attempt should
                        // be retired.
                        continue;
                    }
                    else if (bytesRead > 0)
                    {
                        if (!SctpPacket.VerifyChecksum(recvBuffer, 0, bytesRead))
                        {
                            logger.LogWarning($"SCTP packet received on DTLS transport dropped due to invalid checksum.");
                        }
                        else
                        {
                            var pkt = SctpPacket.Parse(recvBuffer, 0, bytesRead);

                            if (pkt.Chunks.Any(x => x.KnownType == SctpChunkType.INIT))
                            {
                                var initChunk = pkt.Chunks.First(x => x.KnownType == SctpChunkType.INIT) as SctpInitChunk;
                                logger.LogDebug($"SCTP INIT packet received, initial tag {initChunk.InitiateTag}, initial TSN {initChunk.InitialTSN}.");

                                GotInit(pkt, null);
                            }
                            else if (pkt.Chunks.Any(x => x.KnownType == SctpChunkType.COOKIE_ECHO))
                            {
                                // The COOKIE ECHO chunk is the 3rd step in the SCTP handshake when the remote party has
                                // requested a new association be created.
                                var cookie = base.GetCookie(pkt);

                                if (cookie.IsEmpty())
                                {
                                    logger.LogWarning($"SCTP error acquiring handshake cookie from COOKIE ECHO chunk.");
                                }
                                else
                                {
                                    RTCSctpAssociation.GotCookie(cookie);

                                    if (pkt.Chunks.Count() > 1)
                                    {
                                        // There could be DATA chunks after the COOKIE ECHO chunk.
                                        RTCSctpAssociation.OnPacketReceived(pkt);
                                    }
                                }
                            }
                            else
                            {
                                RTCSctpAssociation.OnPacketReceived(pkt);
                            }
                        }
                    }
                    else if (_isClosed)
                    {
                        // The DTLS transport has been closed or is no longer available.
                        logger.LogWarning($"SCTP the RTCSctpTransport DTLS transport returned an error.");
                        break;
                    }
                }
                catch (ApplicationException appExcp)
                {
                    // Treat application exceptions as recoverable, things like SCTP packet parse failures.
                    logger.LogWarning($"SCTP error processing RTCSctpTransport receive. {appExcp.Message}");
                }
                catch (TlsFatalAlert alert) when(alert.InnerException is SocketException)
                {
                    var sockExcp = alert.InnerException as SocketException;

                    logger.LogWarning($"SCTP RTCSctpTransport receive socket failure {sockExcp.SocketErrorCode}.");
                    break;
                }
                catch (Exception excp)
                {
                    logger.LogError($"SCTP fatal error processing RTCSctpTransport receive. {excp}");
                    break;
                }
            }

            if (!_isClosed)
            {
                logger.LogWarning($"SCTP association {RTCSctpAssociation.ID} receive thread stopped.");
            }

            SetState(RTCSctpTransportState.Closed);
        }