/// <summary>
 /// Initializes a new instance of the ReliableUdpConnectionRecord
 /// to send message
 /// </summary>
 /// <param name="key">EndPoint and TransmissionId key</param>
 /// <param name="tcb">Connection control block. <see cref="ReliableUdpConnectionControlBlock"/></param>
 /// <param name="reliableUdpMessage"><see cref="ReliableUdpMessage"/> message to send.</param>
 /// <param name="cToken">CancellationToken</param>
 /// <param name="asyncResult"><see cref="AsyncResultSendMessage"/></param>
 /// <exception cref="ArgumentNullException"><paramref name="key"/> or <paramref name="tcb"/> is a null reference</exception>
 public ReliableUdpConnectionRecord(Tuple <EndPoint, Int32> key, ReliableUdpConnectionControlBlock tcb,
                                    ReliableUdpMessage reliableUdpMessage, CancellationToken cToken, AsyncResultSendMessage asyncResult)
     : this(key, tcb)
 {
     if (reliableUdpMessage == null)
     {
         throw new ArgumentNullException("reliableUdpMessage");
     }
     if (reliableUdpMessage.Body == null || reliableUdpMessage.Body.Length == 0)
     {
         throw new ArgumentNullException("reliableUdpMessage", "reliableUdpMessage.Body can not be null.");
     }
     CToken                      = cToken;
     this.AsyncResult            = asyncResult;
     this.ReliableUdpMessageType = reliableUdpMessage.Type;
     this.IsNoAnswerNeeded       = reliableUdpMessage.NoAsk;
     //RU: добавляем содержимое ReliableUdpMessage в словарь ReliableUdpConnectionControlBlock'a
     //EN: fills the array of outcoming message with message bytes
     this.OutcomingStream = Tcb.OutcomingStreams.GetOrAdd(key, reliableUdpMessage.Body);
     //RU: Расчитываем количество пакетов необходимых для отправки сообщения
     this.NumberOfPackets = (int)Math.Ceiling((double)((double)OutcomingStream.Length / (double)this.BufferSize));
     //RU: переключаем состояние на отправку первого пакета
     //EN: set initial state
     this.State = Tcb.States.FirstPacketSending;
 }
        private IAsyncResult BeginSendTask(WrappedBeginSendParameters obj, AsyncCallback callback, Object state)
        {
            //RU: упаковали данные в собственный AsyncResult
            //EN: wrapped data in our own AsyncResult
            AsyncResultSendMessage ar = new AsyncResultSendMessage(obj.ReliableUdpMessage, obj.RemoteEndPoint, obj.Token, callback, state, this);

            ThreadPool.QueueUserWorkItem(StartTransmissionHelper, ar);
            return(ar);
        }
        /// <summary>
        /// Sends the <paramref name="reliableUdpMessage"/> asynchronously to the specified endpoint.
        /// </summary>
        /// <param name="reliableUdpMessage"><see cref="ReliableUdpMessage"/> message to send.</param>
        /// <param name="remoteEndPoint">Ip endpoint of recipient of the message.</param>
        /// <param name="callback">The <see cref="AsyncCallback"/> delegate.</param>
        /// <param name="state">An object that contains state information for this request.</param>
        /// <returns>An IAsyncResult that references the asynchronous send.</returns>
        public IAsyncResult BeginSend(ReliableUdpMessage reliableUdpMessage,
                                      IPEndPoint remoteEndPoint, AsyncCallback callback, Object state)
        {
            //RU: упаковали данные в собственный AsyncResult
            //EN: wrapped data in our own AsyncResult
            AsyncResultSendMessage ar = new AsyncResultSendMessage(reliableUdpMessage, remoteEndPoint, CancellationToken.None, callback, state, this);

            ThreadPool.QueueUserWorkItem(StartTransmissionHelper, ar);
            return(ar);
        }
        /// <summary>
        /// Creates new ReliableUdpConnectionRecord and starts finite state automaton
        /// </summary>
        /// <param name="reliableUdpMessage"><see cref="ReliableUdpMessage"/> message to send.</param>
        /// <param name="endPoint">Ip endpoint of recipient of the message.</param>
        /// <param name="asyncResult"><see cref="AsyncResultSendMessage"/> object</param>
        /// <exception cref="ArgumentNullException"></exception>
        /// <exception cref="ArgumentOutOfRangeException"></exception>
        /// <exception cref="ArgumentException"></exception>
        /// <exception cref="ReliableUdpConfigurationException"></exception>
        /// <returns></returns>
        private void StartTransmission(ReliableUdpMessage reliableUdpMessage, EndPoint endPoint, CancellationToken cToken,
                                       AsyncResultSendMessage asyncResult)
        {
            try
            {
                if (m_isListenerStarted == 0)
                {
                    if (this.LocalEndpoint == null)
                    {
                        throw new ArgumentNullException("", "You must use constructor with parameters or start listener before sending message");
                    }
                    StartListener(LocalEndpoint);
                }
                //RU: создаем ключ для словаря, на основе EndPoint и ReliableUdpHeader.TransmissionId
                //EN: create key based on EndPoint and ReliableUdpHeader.TransmissionId for dictionary
                byte[] transmissionId = new byte[4];

                //RU: только криптопровайдер! random выдает одинаковые номера для двух одновременно созданных TCB
                //EN: should use only cryptographic random function. System.Random gives the same values for two
                //EN: or more ConnnectionControlBlock created at the same time
                m_randomCrypto.GetBytes(transmissionId);
                Tuple <EndPoint, Int32> key = new Tuple <EndPoint, Int32>(endPoint, BitConverter.ToInt32(transmissionId, 0));
                Debug.WriteLine("Transmission Id is {0}", key.Item2);
                //EN: make two attemps to add key into dictionary
                if (!m_listOfHandlers.TryAdd(key, new ReliableUdpConnectionRecord(key, this, reliableUdpMessage, cToken, asyncResult)))
                {
                    m_randomCrypto.GetBytes(transmissionId);
                    key = new Tuple <EndPoint, Int32>(endPoint, BitConverter.ToInt32(transmissionId, 0));
                    if (!m_listOfHandlers.TryAdd(key, new ReliableUdpConnectionRecord(key, this, reliableUdpMessage, cToken, asyncResult)))
                    {
                        throw new ArgumentException("Pair TransmissionId & EndPoint is already exists in the dictionary");
                    }
                }
                //RU: запустили состояние в обработку.
                //EN: run state handling
                m_listOfHandlers[key].State.SendPacket(m_listOfHandlers[key]);
            }
            //RU: Все исключения в состояниях обрабатываются отдельно
            //RU: здесь ловятся только те, которые могут появиться до запуска конечного автомата
            //EN: All exceptions that raised in states are handled separately
            catch (ArgumentNullException ex)
            {
                if (asyncResult != null)
                {
                    asyncResult.SetAsCompleted(ex);
                }
                else
                {
                    throw;
                }
            }
            catch (ArgumentOutOfRangeException ex)
            {
                if (asyncResult != null)
                {
                    asyncResult.SetAsCompleted(ex);
                }
                else
                {
                    throw;
                }
            }
            catch (ArgumentException ex)
            {
                if (asyncResult != null)
                {
                    asyncResult.SetAsCompleted(ex);
                }
                else
                {
                    throw;
                }
            }
            catch (ReliableUdpConfigurationException ex)
            {
                if (asyncResult != null)
                {
                    asyncResult.SetAsCompleted(ex);
                }
                else
                {
                    throw;
                }
            }
        }
        /// <summary>
        /// Wrapper of StartTransmission method
        /// </summary>
        /// <param name="asyncResult">Parameter to pass through thread pool</param>
        private void StartTransmissionHelper(Object asyncResult)
        {
            AsyncResultSendMessage ar = (AsyncResultSendMessage)asyncResult;

            StartTransmission(ar.ReliableUdpMessage, ar.EndPoint, ar.Token, ar);
        }
        /// <summary>
        /// Ends a pending asynchronous send.
        /// </summary>
        /// <param name="asyncResult">An IAsyncResult that stores state information for this asynchronous operation.
        /// </param>
        /// <returns><c>true</c> if the message was successfully sent.
        /// <c>false</c> if sending the message was interrupted on a timeout.
        /// if <see cref="ReliableUdpMessage.NoAsk"/> is set to <c>true</c> the message can be delivered
        /// damaged even if returned result equals <c>true</c></returns>
        /// <exception cref="ArgumentNullException"></exception>
        /// <exception cref="ArgumentOutOfRangeException"></exception>
        /// <exception cref="ArgumentException"></exception>
        /// <exception cref="ReliableUdpConfigurationException"></exception>
        /// <exception cref="ObjectDisposedException"></exception>
        /// <exception cref="InvalidOperationException"></exception>
        /// <exception cref="SocketException"></exception>
        public bool EndSend(IAsyncResult asyncResult)
        {
            AsyncResultSendMessage ar = (AsyncResultSendMessage)asyncResult;

            return(AsyncResultSendMessage.EndInvoke(ar, this));
        }