コード例 #1
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);
     }
 }
コード例 #2
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);
            }
        }
コード例 #3
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);
            }
        }
コード例 #4
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);
            }
        }