/// <summary>
        /// Handles current async responce. Starts processing of udp payload in finite state automaton
        /// </summary>
        /// <param name="ar">An IAsyncResult that stores state information for this asynchronous operation.</param>
        /// <exception cref="ArgumentNullException"></exception>
        /// <exception cref="ArgumentException"></exception>
        /// <exception cref="ObjectDisposedException"></exception>
        /// <exception cref="InvalidOperationException"></exception>
        /// <exception cref="SocketException"></exception>
        /// <exception cref="ReliableUdpConfigurationException"></exception>
        private void EndReceive(IAsyncResult ar)
        {
            bool hasError = true;

            try
            {
                EndPoint connectedClient = new IPEndPoint(IPAddress.Any, 0);
                int      bytesRead       = this.m_socketIn.EndReceiveFrom(ar, ref connectedClient);

                //RU: клиент присоединен, готовы принимать следующего
                //EN: client is connected. Start next receive
                Receive();
                //RU: т.к. простейший способ решить вопрос с буфером - получить ссылку на него
                //RU: из IAsyncResult.AsyncState
                //EN: The simplest way to deal with buffer is to get it from IAsyncResult.AsyncState
                byte[] bytes = ((byte[])ar.AsyncState).Slice(0, bytesRead);

                //RU: получаем заголовок пакета
                //EN: getting header
                ReliableUdpHeader header;
                if (!ReliableUdpStateTools.ReadReliableUdpHeader(bytes, out header))
                {
                    //throw new ArgumentException("bad packet header");
                    //RU: не генерируем исключение, а просто отбрасываем пакет
                    //EN: do not throw an exception, just drop this packet
                    hasError = false;
                    return;
                }

                Tuple <EndPoint, Int32>     key    = new Tuple <EndPoint, Int32>(connectedClient, header.TransmissionId);
                ReliableUdpConnectionRecord record = m_listOfHandlers.GetOrAdd(key,
                                                                               new ReliableUdpConnectionRecord(key, this,
                                                                                                               header.
                                                                                                               ReliableUdpMessageType));
                //EN: run state handling
                record.State.ReceivePacket(record, header, bytes);
                hasError = false;
            }
            //RU: EndReceive создает потоки с обработкой каждого пакета
            //RU: завершать его из-за возможных исключений при работе
            //RU: конечного автомата нецелесообразно, поэтому конечный
            //RU: автомат инкапсулирует ошибки и передает их в EndInvoke
            //RU: EndReceive может завершиться
            //RU: только если произойдет действительно критическое событие
            //RU: например ошибка сокета, в таком случае мы освобождаем
            //RU: ресурсы ConnectionControlBlock'а
            //EN: EndReceive method creates threads where packets are handling
            //EN: we shouldn't stop it if we get an exception in finite state
            //EN: automaton. So the best idea is to pass this exceptions
            //EN: directly to invoker.
            //EN: EndReceive can stop if some critical action will raise in
            //EN: its functions, f.e. socket exceptions on EndReceiveFrom method
            finally
            {
                if (hasError)
                {
                    this.Dispose();
                }
            }
        }
示例#2
0
        public override void ProcessPackets(ReliableUdpConnectionRecord connectionRecord)
        {
            try
            {
                System.Threading.Monitor.Enter(connectionRecord.LockerReceive);
                //RU: Переход в этот метод всегда инициируется из состояний Assembling
                //RU: при прохождении проверки и получении пакета с флагом LastMessage.
                //EN: Transition to this method is always initiated from Assembling state
                //EN: when checking is passed successfully and packet with LastMessage flag
                //EN: received
                Interlocked.Increment(ref connectionRecord.IsDone);

                //RU: т.к. для избежания случая недостави последнего подтверждения мы
                //RU: реализуем ресурсы по срабатыванию Dispose таймера, то
                //RU: здесь мы лишь останавливаем WaitForPackets таймер

                if (connectionRecord.WaitForPacketsTimer != null)
                {
                    connectionRecord.WaitForPacketsTimer.Dispose();
                }
                //RU: и передаем сообщение подписчикам
                ReliableUdpStateTools.CreateMessageFromMemoryStream(connectionRecord);
            }
            catch (ObjectDisposedException ex)
            {
                SetAsError(connectionRecord, ex);
            }
            finally
            {
                System.Threading.Monitor.Exit(connectionRecord.LockerReceive);
            }
        }
示例#3
0
        /// <summary>
        /// Invokes on timer
        /// </summary>
        /// <param name="connectionRecord"></param>
        /// <returns></returns>
        public override void ProcessPackets(ReliableUdpConnectionRecord connectionRecord)
        {
            try
            {
                System.Threading.Monitor.Enter(connectionRecord.LockerReceive);
                if (connectionRecord.IsDone != 0)
                {
                    return;
                }
                //Debug.WriteLine( "In timer. Means no ask received" );
                //RU: отправляем повторно последний пакет
                //RU: ( в случае восстановления соединения узел-приемник заново отправит запросы, которые до него не дошли)
                //EN: send the last sent message
                //EN: if connection will be established again the recepient will send again all request for lost packets
                ReliableUdpStateTools.SendPacket(connectionRecord,
                                                 ReliableUdpStateTools.RetransmissionCreateUdpPayload(connectionRecord,
                                                                                                      connectionRecord.SndNext -
                                                                                                      1));

                //RU: включаем таймер CloseWait
                //EN: start closewait timer
                StartCloseWaitTimer(connectionRecord);
            }
            catch (ArgumentNullException ex)
            {
                SetAsError(connectionRecord, ex);
            }
            catch (ArgumentOutOfRangeException ex)
            {
                SetAsError(connectionRecord, ex);
            }
            catch (ArgumentException ex)
            {
                SetAsError(connectionRecord, ex);
            }
            catch (ObjectDisposedException ex)
            {
                SetAsError(connectionRecord, ex);
            }
            catch (InvalidOperationException ex)
            {
                SetAsError(connectionRecord, ex);
            }
            catch (System.Net.Sockets.SocketException ex)
            {
                SetAsError(connectionRecord, ex);
            }
            finally
            {
                System.Threading.Monitor.Exit(connectionRecord.LockerReceive);
            }
        }
示例#4
0
 public override void ReceivePacket(ReliableUdpConnectionRecord connectionRecord, ReliableUdpHeader header,
                                    byte[] payload)
 {
     try
     {
         System.Threading.Monitor.Enter(connectionRecord.LockerReceive);
         //RU: сюда могут попасть либо задержавшиеся дубликаты
         //RU: либо повторная отправка последнего пакета в связи с тем,
         //RU: что последний ack не дошел до отправителя
         if (header.Flags.HasFlag(ReliableUdpHeaderFlags.LastPacket))
         {
             ReliableUdpStateTools.SendAcknowledgePacket(connectionRecord);
         }
     }
     finally
     {
         System.Threading.Monitor.Exit(connectionRecord.LockerReceive);
     }
 }
示例#5
0
 public override void SendPacket(ReliableUdpConnectionRecord connectionRecord)
 {
     //Debug.WriteLine( "#{0} block sending...", connectionRecord.SndNext );
     //RU: отправляем блок пакетов
     //EN: sending block of packets
     for (connectionRecord.PacketCounter = 0;
          connectionRecord.PacketCounter < connectionRecord.WindowSize &&
          connectionRecord.SndNext < connectionRecord.NumberOfPackets;
          connectionRecord.PacketCounter++)
     {
         ReliableUdpHeader header = ReliableUdpStateTools.CreateReliableUdpHeader(connectionRecord);
         ReliableUdpStateTools.SendPacket(connectionRecord,
                                          ReliableUdpStateTools.CreateUdpPayload(connectionRecord, header));
         connectionRecord.SndNext++;
     }
     //RU: на случай большого окна передачи, перезапускаем таймер после отправки
     connectionRecord.WaitForPacketsTimer.Change(connectionRecord.ShortTimerPeriod, -1);
     if (connectionRecord.CloseWaitTimer != null)
     {
         connectionRecord.CloseWaitTimer.Change(-1, -1);
     }
 }
示例#6
0
        public override void ProcessPackets(ReliableUdpConnectionRecord connectionRecord)
        {
            #region Some comments

            //RU: В этот метод мы можем попасть в трех случаях
            //RU: Первый:
            //RU: у нас пришел последний пакет и нам необходимо проверить
            //RU: остались ли у нас какие-либо не пришедшие пакеты.
            //RU: если все пришли - переключаемся на сборку сообщения
            //RU: если есть потери - составляем список потерянных пакетов
            //RU: Второй случай:
            //RU: сработал таймер - а следовательно у нас какие-то пакеты
            //RU: не пришли вовремя. Составляем список потерянных пакетов
            //RU: Третий случай - сработал таймер, все пакеты пришли вовремя
            //RU: но ack пакет на блок данных не дошел до отправителя
            //EN: We can get into this method in three cases
            //EN: First case:
            //EN: we received last packet and we have to test for lost packets
            //EN: Second case:
            //EN: WaitForPacketsTimer fired and we have some lost packets
            //EN: Third case:
            //EN: WaitForPacketsTimer fired, acknowledge packet lost

            #endregion

            try
            {
                System.Threading.Monitor.Enter(connectionRecord.LockerReceive);
                if (connectionRecord.IsDone != 0)
                {
                    return;
                }
                //Debug.WriteLine( "In receiver checking" );
                if (!ReliableUdpStateTools.CheckForNoPacketLoss(connectionRecord, connectionRecord.IsLastPacketReceived != 0))
                {
                    //RU: есть потерянные пакеты, отсылаем запросы на них
                    //EN: we have lost packets. Send request for each
                    foreach (int seqNum in connectionRecord.LostPackets)
                    {
                        if (seqNum != 0)
                        {
                            //Debug.WriteLine("SENDING REQUEST TO {0} FOR {1}\nWindowLowerBound {2}, RcvCurrent {3}\nNumberOfPackets{4}",
                            //  connectionRecord.TransmissionId, seqNum, connectionRecord.WindowLowerBound, connectionRecord.RcvCurrent, connectionRecord.NumberOfPackets );
                            ReliableUdpStateTools.SendAskForLostPacket(connectionRecord, seqNum);
                        }
                    }
                    //RU: устанавливаем таймер во второй раз, для повторной попытки передачи
                    if (!connectionRecord.TimerSecondTry)
                    {
                        connectionRecord.WaitForPacketsTimer.Change(connectionRecord.ShortTimerPeriod, -1);
                        connectionRecord.TimerSecondTry = true;
                        return;
                    }
                    //RU: если после двух попыток срабатываний WaitForPacketTimer
                    //RU: не удалось получить пакеты - запускаем таймер завершения соединения
                    StartCloseWaitTimer(connectionRecord);
                }
                else if (connectionRecord.IsLastPacketReceived != 0)
                //RU: успешная проверка
                //EN: successful check
                {
                    //RU: высылаем подтверждение о получении данных окна
                    //EN: send last ask
                    //Debug.WriteLine("Sending last ask for {0}", connectionRecord.TransmissionId);
                    ReliableUdpStateTools.SendAcknowledgePacket(connectionRecord);
                    connectionRecord.State = connectionRecord.Tcb.States.Completed;
                    connectionRecord.State.ProcessPackets(connectionRecord);
                    //RU: вместо моментальной реализации ресурсов
                    //RU: запускаем таймер, на случай, если
                    //RU: если последний ack не дойдет до отправителя и он запросит его снова.
                    //RU: по срабатыванию таймера - реализуем ресурсы
                    //RU: !!в состоянии Completed метод таймера переопределен
                    StartCloseWaitTimer(connectionRecord);
                }
                //EN: in this case we have lost acknowledge packet
                //RU: это случай, когда ack на блок пакетов был потерян
                else
                {
                    if (!connectionRecord.TimerSecondTry)
                    {
                        ReliableUdpStateTools.SendAcknowledgePacket(connectionRecord);
                        connectionRecord.WaitForPacketsTimer.Change(connectionRecord.ShortTimerPeriod, -1);
                        connectionRecord.TimerSecondTry = true;
                        return;
                    }
                    //RU: не удалось получить пакеты - запускаем таймер завершения соединения
                    StartCloseWaitTimer(connectionRecord);
                }
            }
            catch (ArgumentNullException ex)
            {
                SetAsError(connectionRecord, ex);
            }
            catch (ArgumentOutOfRangeException ex)
            {
                SetAsError(connectionRecord, ex);
            }
            catch (ArgumentException ex)
            {
                SetAsError(connectionRecord, ex);
            }
            catch (ObjectDisposedException ex)
            {
                SetAsError(connectionRecord, ex);
            }
            catch (InvalidOperationException ex)
            {
                SetAsError(connectionRecord, ex);
            }
            catch (System.Net.Sockets.SocketException ex)
            {
                SetAsError(connectionRecord, ex);
            }
            finally
            {
                System.Threading.Monitor.Exit(connectionRecord.LockerReceive);
            }
        }
示例#7
0
        public override void ReceivePacket(ReliableUdpConnectionRecord connectionRecord, ReliableUdpHeader header,
                                           byte[] payload)
        {
            #region Some comments

            //RU: ПРИМЕЧАНИЯ:
            //RU: важно понимать разницу между подтверждениями о получении
            //RU: и запросами потерянных пакетов.
            //RU: когда отправляем подтверждение, то указываем номер последовательности,
            //RU: которая должна прийти следующей
            //RU: когда отправляем запрос о получении потерянных пакетов
            //RU: то указываем номер той конкретной последовательности которая потерялась
            //EN: NOTES:
            //EN: it is important to understand the difference between the acknowledgment
            //EN: of the receipt of packets and requests for lost packets.
            //EN: When we send acknowledge we set the next expected packet number
            //EN: When we send request for lost packet we set concrete packet number of lost packet


            //RU: Основной класс собирающий пакеты
            //RU: Шаг 1. Записываем пакет в память
            //RU: Шаг 2. Проверяем счетчик пакетов:
            //RU: Если счетчик пакетов достиг значения RcvPacketsMax
            //RU: и если проверка пакетов завершилась успешно:
            //RU: отправляем подтверждение о получении
            //RU: иначе: запускаем процедуру запроса пакетов

            //RU: примеры
            //EN: Examples
            // S(Sender), R(Receiver), udp1 (udp packet, where 1 - seq.number), L - Flags.LastMessage, ReceiverBuffer equals 5
            //
            // Good transmission, without errors:
            // S --> udp6 -->R
            // S --> udp7 -->R
            // S --> udp8 -->R
            // S --> udp9 -->R
            // S --> udp10L -->R
            // on Receiver (we just get udp10L packet):
            // check if  Header.PacketNumber > connectionRecord.RcvWindowStartPacket (true)
            // check if we have no such record in our RcvWindowControlArray (true)
            // check if Flags has LastMessage. (true)
            // check if PacketCounter == RecieverBuffer to perform checking for packets loss and dublicates (true - no errors)
            // if true && true && true && true => go to the state Completed and RunItsOwnChecking
            //
            // Transmission with duplicates, without errors:
            // S --> udp6 -->R
            // S --> udp7 -->R
            // S --> udp8 -->R
            // S --> udp8 -->R
            // on Receiver (we just get duplicate of udp8 packet):
            // check if  Header.PacketNumber > connectionRecord.RcvWindowStartPacket (true)
            // check if we have no such record in our RcvWindowControlArray (false - means we received this packet before)
            //  -> Decrement PacketCounter ( because it was incremented in WritePacketData )
            //  -> get away this packet
            //
            // Transmission with duplicates, without errors:
            // S --> udp6 -->R
            // S --> udp7 -->R
            // S --> udp8 -->R
            // S --> udp5 -->R
            // on Receiver (we just get duplicate of udp5 packet, who has been received in previous checking session):
            // check if  Header.PacketNumber > connectionRecord.RcvWindowStartPacket (false)
            //  -> Decrement PacketCounter ( because it was incremented in WritePacketData )
            //  -> get away this packet

            #endregion

            try
            {
                System.Threading.Monitor.Enter(connectionRecord.LockerReceive);
                if (connectionRecord.IsDone != 0)
                {
                    return;
                }

                #region Processing packets with NoAsk flag

                if (header.Flags.HasFlag(ReliableUdpHeaderFlags.NoAsk))
                {
                    //RU: сбрасываем таймер
                    connectionRecord.CloseWaitTimer.Change(connectionRecord.LongTimerPeriod, -1);
                    //EN: Write data to byte array.
                    ReliableUdpStateTools.WritePacketData(connectionRecord, header, payload);
                    //RU: если получили пакет с последним флагом - завершаем
                    //EN: if we received packet with LastPacket flag - go to Completed state
                    if (header.Flags.HasFlag(ReliableUdpHeaderFlags.LastPacket))
                    {
                        connectionRecord.State = connectionRecord.Tcb.States.Completed;
                        connectionRecord.State.ProcessPackets(connectionRecord);
                    }
                    return;
                }

                #endregion

                //RU: расчет конечной границы окна, чтобы избежать ошибок с записью/чтением в несуществующий индекс
                int windowHighestBound = Math.Min((connectionRecord.WindowLowerBound + connectionRecord.WindowSize - 1),
                                                  (connectionRecord.NumberOfPackets - 1));
                //EN: drop this packet if true. It is not in receive window
                if (header.PacketNumber < connectionRecord.WindowLowerBound ||
                    header.PacketNumber > (windowHighestBound))
                {
                    return;
                }

                //EN: drop this packet if true. It is duplicate
                if (connectionRecord.WindowControlArray.Contains(header.PacketNumber))
                {
                    return;
                }

                //EN: Write data to stream.
                ReliableUdpStateTools.WritePacketData(connectionRecord, header, payload);
                //RU: увеличиваем счетчик пакетов
                //EN: increase packet counter
                connectionRecord.PacketCounter++;

                //RU: записываем в массив управления окном текущий номер пакета
                //EN: write to control array current packet number
                connectionRecord.WindowControlArray[header.PacketNumber - connectionRecord.WindowLowerBound] =
                    header.PacketNumber;

                //RU: устанавливаем наибольший пришедший пакет
                //EN: set the maximum incoming packet
                if (header.PacketNumber > connectionRecord.RcvCurrent)
                {
                    connectionRecord.RcvCurrent = header.PacketNumber;
                }

                //RU: перезапуск таймеров
                connectionRecord.TimerSecondTry = false;
                connectionRecord.WaitForPacketsTimer.Change(connectionRecord.ShortTimerPeriod, -1);
                if (connectionRecord.CloseWaitTimer != null)
                {
                    connectionRecord.CloseWaitTimer.Change(-1, -1);
                }

                //RU: устанавливаем IsLastPacketReceived - что пришел последний пакет
                //EN: if last packet has arrived than increase IsLastPacketReceived
                if (header.Flags.HasFlag(ReliableUdpHeaderFlags.LastPacket))
                {
                    Interlocked.Increment(ref connectionRecord.IsLastPacketReceived);
                }
                //RU: если нам пришли все пакеты окна, то сбрасываем счетчик
                //RU: и высылаем пакет подтверждение
                //EN: if we received all packets for the current window
                //EN: reset the counter and send acknowledge packet
                else if (connectionRecord.PacketCounter == connectionRecord.WindowSize)
                {
                    //RU: сбрасываем счетчик.
                    connectionRecord.PacketCounter = 0;
                    //RU: сдвинули окно передачи
                    //EN: move window
                    connectionRecord.WindowLowerBound += connectionRecord.WindowSize;
                    //RU: обнуление массива управления передачей
                    connectionRecord.WindowControlArray.Nullify();
                    ReliableUdpStateTools.SendAcknowledgePacket(connectionRecord);
                }

                //RU: если последний пакет уже имеется
                //EN: if we received last packet
                if (Thread.VolatileRead(ref connectionRecord.IsLastPacketReceived) != 0)
                {
                    //RU: проверяем пакеты
                    //EN: check the packets
                    ProcessPackets(connectionRecord);
                }
            }
            catch (ArgumentNullException ex)
            {
                SetAsError(connectionRecord, ex);
            }
            catch (ArgumentOutOfRangeException ex)
            {
                SetAsError(connectionRecord, ex);
            }
            catch (ArgumentException ex)
            {
                SetAsError(connectionRecord, ex);
            }
            catch (ObjectDisposedException ex)
            {
                SetAsError(connectionRecord, ex);
            }
            catch (InvalidOperationException ex)
            {
                SetAsError(connectionRecord, ex);
            }
            catch (System.Net.Sockets.SocketException ex)
            {
                SetAsError(connectionRecord, ex);
            }
            finally
            {
                System.Threading.Monitor.Exit(connectionRecord.LockerReceive);
            }
        }
示例#8
0
        public override void ReceivePacket(ReliableUdpConnectionRecord connectionRecord, ReliableUdpHeader header,
                                           byte[] payload)
        {
            try
            {
                System.Threading.Monitor.Enter(connectionRecord.LockerReceive);
                //RU: Шаг1. первым делом, при получении пакета с флагом FirstMessage
                //RU: мы должны создать в структуре TCB новый поток в памяти,
                //RU: для записи туда полученных байт.
                //RU: Шаг.2 увеличить счетчик принятых пакетов, в дальнейшем
                //RU: он нам понадобится для расчета верности полученных данных
                //RU: Шаг 3.Записать данные из полученного пакета в memoryStream
                //RU: Шаг 4.Отправить пакет подтверждение
                //RU: Шаг 5.Включить таймер ожидания пакетов, при срабатывании которого
                //RU: переходим в состоянии длительного ожидания пакетов ( WaitForPackets )

                if (!header.Flags.HasFlag(ReliableUdpHeaderFlags.FirstPacket))
                {
                    //EN: drop this packet;
                    return;
                }
                //RU: комбинация двух флагов - FirstPacket и LastPacket - говорит что у нас единственное сообщение
                //EN: combination of flags FirstPacket and LastPacket means that we received a single packet message
                if (header.Flags.HasFlag(ReliableUdpHeaderFlags.FirstPacket) &
                    header.Flags.HasFlag(ReliableUdpHeaderFlags.LastPacket))
                {
                    ReliableUdpStateTools.CreateMessageFromSinglePacket(connectionRecord, header,
                                                                        payload.Slice(ReliableUdpHeader.Length, payload.Length));
                    //RU: отправляем пакет подтверждение
                    //EN: send acknowledge packet
                    if (!header.Flags.HasFlag(ReliableUdpHeaderFlags.NoAsk))
                    {
                        ReliableUdpStateTools.SendAcknowledgePacket(connectionRecord);
                    }
                    SetAsCompleted(connectionRecord);
                    Debug.WriteLine("Done!");
                    return;
                }

                //RU: поминим, что by design все packet numbers начинаются с 0;
                //EN: all packet numbers of any transmission start from 0 by design
                if (header.PacketNumber != 0)
                {
                    //EN: drop this packet;
                    return;
                }

                ReliableUdpStateTools.InitIncomingBytesStorage(connectionRecord, header);
                ReliableUdpStateTools.WritePacketData(connectionRecord, header, payload);

                //RU: считаем кол-во пакетов, которые должны прийти
                connectionRecord.NumberOfPackets =
                    (int)
                    Math.Ceiling((double)((double)connectionRecord.IncomingStream.Length / (double)connectionRecord.BufferSize));

                //RU: записываем номер последнего полученного пакета (0)
                connectionRecord.RcvCurrent = header.PacketNumber;

                //RU: после сдвинули окно приема на 1
                //EN: shift window by 1
                connectionRecord.WindowLowerBound++;

                connectionRecord.State = connectionRecord.Tcb.States.Assembling;

                //RU: если не требуется подтверждение
                //RU: запускаем таймер который высвободит все структуры
                //EN: if we have NoAsk transmission
                //EN: Start a dispose timer
                if (header.Flags.HasFlag(ReliableUdpHeaderFlags.NoAsk))
                {
                    connectionRecord.CloseWaitTimer = new Timer(DisposeByTimeout, connectionRecord,
                                                                connectionRecord.ShortTimerPeriod, -1);
                }
                else
                {
                    ReliableUdpStateTools.SendAcknowledgePacket(connectionRecord);
                    //EN: Start WaitForPacketsTimer
                    connectionRecord.WaitForPacketsTimer = new Timer(CheckByTimer, connectionRecord,
                                                                     connectionRecord.ShortTimerPeriod, -1);
                }
            }
            catch (ArgumentNullException ex)
            {
                SetAsError(connectionRecord, ex);
            }
            catch (ArgumentOutOfRangeException ex)
            {
                SetAsError(connectionRecord, ex);
            }
            catch (ArgumentException ex)
            {
                SetAsError(connectionRecord, ex);
            }
            catch (ObjectDisposedException ex)
            {
                SetAsError(connectionRecord, ex);
            }
            catch (InvalidOperationException ex)
            {
                SetAsError(connectionRecord, ex);
            }
            catch (System.Net.Sockets.SocketException ex)
            {
                SetAsError(connectionRecord, ex);
            }
            finally
            {
                System.Threading.Monitor.Exit(connectionRecord.LockerReceive);
            }
        }
示例#9
0
        public override void ReceivePacket(ReliableUdpConnectionRecord connectionRecord, ReliableUdpHeader header,
                                           byte[] payload)
        {
            try
            {
                //RU: ПРИМЕЧАНИЯ:
                //RU: важно понимать разницу между подтверждениями о получении
                //RU: и запросами потерянных пакетов.
                //RU: когда отправляем подтверждение, то указываем номер последовательности,
                //RU: которая должна прийти следующей
                //RU: когда отправляем запрос о получении потерянных пакетов
                //RU: то указываем номер той конкретной последовательности которая потерялась
                //EN: NOTES:
                //EN: it is important to understand the difference between the acknowledgment
                //EN: of the receipt of packets and requests for lost packets.
                //EN: When we send acknowledge we set the next expected packet number
                //EN: When we send request for lost packet we set concrete packet number of lost packet
                System.Threading.Monitor.Enter(connectionRecord.LockerReceive);

                //EN: if cancellation requested - stop and release resources
                connectionRecord.CToken.ThrowIfCancellationRequested();

                if (connectionRecord.IsDone != 0)
                {
                    return;
                }

                if (!header.Flags.HasFlag(ReliableUdpHeaderFlags.RequestForPacket))
                {
                    //EN: drop packet
                    return;
                }

                //RU: расчет конечной границы окна, чтобы избежать ошибок с записью/чтением в несуществующий индекс
                //RU: в расчете берется граница окна + 1 - для получения подтверждений
                //EN: calculation of the final border of the window.
                int windowHighestBound = Math.Min((connectionRecord.WindowLowerBound + connectionRecord.WindowSize),
                                                  (connectionRecord.NumberOfPackets));

                //RU: проверка на попадание в окно
                //EN: check to get into the reception window
                if (header.PacketNumber < connectionRecord.WindowLowerBound || header.PacketNumber > windowHighestBound)
                {
                    //EN: drop packet
                    return;
                }

                connectionRecord.WaitForPacketsTimer.Change(connectionRecord.ShortTimerPeriod, -1);
                if (connectionRecord.CloseWaitTimer != null)
                {
                    connectionRecord.CloseWaitTimer.Change(-1, -1);
                }

                //RU: проверить на последний пакет:
                //EN: check for last packet
                if (header.PacketNumber == connectionRecord.NumberOfPackets)
                {
                    //RU: передача завершена
                    //EN: transfer completed
                    Debug.WriteLine("Done {0}! ", connectionRecord.TransmissionId);
                    Interlocked.Increment(ref connectionRecord.IsDone);
                    SetAsCompleted(connectionRecord);
                    return;
                }

                //RU: это ответ на первый пакет c подтверждением
                //EN: check for acknowledge of the first packet
                if ((header.Flags.HasFlag(ReliableUdpHeaderFlags.FirstPacket) && header.PacketNumber == 1))
                {
                    //RU: без сдвига окна
                    //EN: don't move window lower bound
                    SendPacket(connectionRecord);
                }
                //RU: пришло подтверждение о получении блока данных
                //EN: check confirmation of delivery of block of packets
                else if (header.PacketNumber == windowHighestBound)
                {
                    //Debug.WriteLine( "#{0} ask received", Header.PacketNumber );
                    //RU: сдвигаем окно
                    //EN: move window
                    connectionRecord.WindowLowerBound += connectionRecord.WindowSize;
                    //RU: обнуляем массив контроля передачи
                    //EN: nullify window control array
                    connectionRecord.WindowControlArray.Nullify();
                    SendPacket(connectionRecord);
                }
                //RU: это запрос на повторную передачу
                //EN: if we are here than it is request for retransmission
                else
                {
                    ReliableUdpStateTools.SendPacket(connectionRecord,
                                                     ReliableUdpStateTools.RetransmissionCreateUdpPayload(connectionRecord,
                                                                                                          header.PacketNumber));
                }
            }
            catch (ArgumentNullException ex)
            {
                SetAsError(connectionRecord, ex);
            }
            catch (ArgumentOutOfRangeException ex)
            {
                SetAsError(connectionRecord, ex);
            }
            catch (ArgumentException ex)
            {
                SetAsError(connectionRecord, ex);
            }
            catch (ObjectDisposedException ex)
            {
                SetAsError(connectionRecord, ex);
            }
            catch (InvalidOperationException ex)
            {
                SetAsError(connectionRecord, ex);
            }
            catch (System.Net.Sockets.SocketException ex)
            {
                SetAsError(connectionRecord, ex);
            }
            catch (OperationCanceledException ex)
            {
                SetAsError(connectionRecord, ex);
            }
            finally
            {
                System.Threading.Monitor.Exit(connectionRecord.LockerReceive);
            }
        }
示例#10
0
        public override void SendPacket(ReliableUdpConnectionRecord connectionRecord)
        {
            try
            {
                System.Threading.Monitor.Enter(connectionRecord.LockerReceive);

                connectionRecord.PacketCounter    = 0;
                connectionRecord.SndNext          = 0;
                connectionRecord.WindowLowerBound = 0;

                #region Sending a message that does not require confirmation of delivery (Flags = NoAsk)

                //RU: если подтверждения не требуется - отправляем все пакеты
                //RU: и высвобождаем ресурсы
                //EN: if confirmation of delivery doesn't requires than
                //EN: send all packets and release the resources
                if (connectionRecord.IsNoAnswerNeeded)
                {
                    //RU: Здесь происходит отправка As Is
                    //RU: при такой отправке возможен вариант
                    //RU: кроме того при большой нагрузке возможна потеря первого пакета (не попадание в буфер приема)
                    //RU: соответственно в таком случае все отправленные с данной TransmissionId
                    //RU: пакеты буду отброшены
                    //EN: Here is "As Is" sending
                    //EN: If in some cases first packet won't arrive on endpoint than all packets of the
                    //EN: current TransmissionId will be droped
                    do
                    {
                        connectionRecord.CToken.ThrowIfCancellationRequested();
                        ReliableUdpStateTools.SendPacket(connectionRecord,
                                                         ReliableUdpStateTools.CreateUdpPayload(connectionRecord,
                                                                                                ReliableUdpStateTools.
                                                                                                CreateReliableUdpHeader(
                                                                                                    connectionRecord)
                                                                                                ));
                        connectionRecord.SndNext++;
                    } while (connectionRecord.SndNext < connectionRecord.NumberOfPackets);
                    SetAsCompleted(connectionRecord);
                    return;
                }

                #endregion

                connectionRecord.CToken.ThrowIfCancellationRequested();
                ReliableUdpHeader header = ReliableUdpStateTools.CreateReliableUdpHeader(connectionRecord);
                ReliableUdpStateTools.SendPacket(connectionRecord,
                                                 ReliableUdpStateTools.CreateUdpPayload(connectionRecord, header));

                //RU: увеличиваем счетчик
                //EN: increase SndNext
                connectionRecord.SndNext++;
                //RU: сдвигаем окно
                //EN: move window lower bound
                connectionRecord.WindowLowerBound++;
                connectionRecord.State = connectionRecord.Tcb.States.SendingCycle;
                //RU: Запускаем таймер
                //EN: Start the timer
                connectionRecord.WaitForPacketsTimer = new Timer(CheckByTimer, connectionRecord,
                                                                 connectionRecord.ShortTimerPeriod, -1);
            }
            catch (ArgumentNullException ex)
            {
                SetAsError(connectionRecord, ex);
            }
            catch (ArgumentOutOfRangeException ex)
            {
                SetAsError(connectionRecord, ex);
            }
            catch (ArgumentException ex)
            {
                SetAsError(connectionRecord, ex);
            }
            catch (ObjectDisposedException ex)
            {
                SetAsError(connectionRecord, ex);
            }
            catch (InvalidOperationException ex)
            {
                SetAsError(connectionRecord, ex);
            }
            catch (System.Net.Sockets.SocketException ex)
            {
                SetAsError(connectionRecord, ex);
            }
            catch (OperationCanceledException ex)
            {
                SetAsError(connectionRecord, ex);
            }
            finally
            {
                System.Threading.Monitor.Exit(connectionRecord.LockerReceive);
            }
        }