Exemplo n.º 1
0
        /// <summary>
        /// Creates header of acknowledge packet
        /// </summary>
        /// <param name="connectionRecord">Current <see cref="ReliableUdpConnectionRecord"/></param>
        /// <param name="isAskForLost"><c>true</c> if we need resending</param>
        /// <param name="seqNum">Lost packet number if <paramref name="isAskForLost"/> is <c>true</c></param>
        /// <returns></returns>
        internal static ReliableUdpHeader CreateAcknowledgeHeader(ReliableUdpConnectionRecord connectionRecord,
                                                                  bool isAskForLost = false, int seqNum = -1)
        {
            ReliableUdpHeader header = new ReliableUdpHeader();

            //RU: запись номера передачи
            header.TransmissionId = connectionRecord.TransmissionId;
            //RU: тип сообщения
            header.ReliableUdpMessageType = connectionRecord.ReliableUdpMessageType;
            //RU: установка Ask флаг
            //EN: set Ask flag
            header.Flags = ReliableUdpHeaderFlags.RequestForPacket;
            //RU: для первого пакета устанавливаем соответствующий флаг
            //EN: set FirstPacket flag for the ask on first packet
            if (connectionRecord.RcvCurrent == 0)
            {
                header.Flags = header.Flags | ReliableUdpHeaderFlags.FirstPacket;
            }
            if (!isAskForLost)
            {
                //RU: установка номера пакета для передачи - означает что ждем следующий пакет
                //EN: it means that we are ready and waiting for next block of data
                header.PacketNumber = connectionRecord.RcvCurrent + 1;
            }
            else
            {
                header.PacketNumber = seqNum;
            }
            return(header);
        }
Exemplo n.º 2
0
        /// <summary>
        /// Creates a package for a resending
        /// </summary>
        /// <param name="connectionRecord">Current <see cref="ReliableUdpConnectionRecord"/> (ConnectionRecord)</param>
        /// <param name="requestedSequence">Lost packet number </param>
        /// <returns>Array of bytes with header and data</returns>
        internal static byte[] RetransmissionCreateUdpPayload(ReliableUdpConnectionRecord connectionRecord,
                                                              int requestedSequence)
        {
            //RU: проверка на последний пакет,
            //EN: check for last packet
            //bool isLast = ((RequestedSequence + 1) - connectionRecord.NumberOfPackets == 0);
            bool isLast = ((requestedSequence + 1) == connectionRecord.NumberOfPackets);
            //RU: установка размера данных для отправки
            //EN: set the size of data for sending
            int length = isLast
                     ? connectionRecord.OutcomingStream.Length - requestedSequence * connectionRecord.BufferSize
                     : connectionRecord.BufferSize;
            //RU: создаем заголовок для пакета
            ReliableUdpHeader header = CreateReliableUdpHeader(connectionRecord.TransmissionId,
                                                               isLast
                                                           ? ReliableUdpHeaderFlags.LastPacket
                                                           : ReliableUdpHeaderFlags.None,
                                                               connectionRecord.ReliableUdpMessageType, requestedSequence, 0);

            byte[] udpPayload = new byte[length + ReliableUdpHeader.Length];
            Array.Copy(header.ToBytes(), udpPayload, ReliableUdpHeader.Length);
            Array.Copy(connectionRecord.OutcomingStream, connectionRecord.BufferSize * requestedSequence, udpPayload,
                       ReliableUdpHeader.Length, length);

            return(udpPayload);
        }
Exemplo n.º 3
0
        /// <summary>
        /// Reads ReliableUdp header
        /// </summary>
        /// <param name="udpPayloadBytes">Payload of udp packet</param>
        /// <param name="header"><see cref="ReliableUdpHeader"/></param>
        /// <returns><c>false</c> if packet is garbage or damaged</returns>
        internal static bool ReadReliableUdpHeader(byte[] udpPayloadBytes,
                                                   out ReliableUdpHeader header)
        {
            header = new ReliableUdpHeader();
            //RU: в случае поврежденного пакета
            //EN: in case of damaged packet
            if (udpPayloadBytes.Length < ReliableUdpHeader.Length)
            {
                return(false);
            }

            header.Flags = (ReliableUdpHeaderFlags)udpPayloadBytes[0];
            header.ReliableUdpMessageType = (ReliableUdpMessageTypes)BitConverter.ToInt16(udpPayloadBytes, 2);
            header.TransmissionId         = BitConverter.ToInt32(udpPayloadBytes, 4);
            header.PacketNumber           = BitConverter.ToInt32(udpPayloadBytes, 8);

            //RU: здесь мы проверяем первый пакет из множества пакетов, передающих сообщение
            //RU: узнаем размер сообщения, размер нам необходим для создания MemoryStream'а
            //EN: here we check first packet with data and get total size of message from
            //EN: Options field
            if (header.Flags.HasFlag(ReliableUdpHeaderFlags.FirstPacket) &&
                (!header.Flags.HasFlag(ReliableUdpHeaderFlags.RequestForPacket))
                )
            {
                header.Options = BitConverter.ToInt32(udpPayloadBytes, 12);

                if (header.Options < 1)
                {
                    //EN: drop damaged packet
                    return(false);
                }
            }
            return(true);
        }
Exemplo n.º 4
0
 /// <summary>
 /// Creates message from single packet
 /// </summary>
 /// <param name="connectionRecord">Current <see cref="ReliableUdpConnectionRecord"/> (ConnectionRecord)</param>
 /// <param name="header"><see cref="ReliableUdpHeader"/></param>
 /// <param name="data">packet data (udp payload - header)</param>
 internal static void CreateMessageFromSinglePacket(ReliableUdpConnectionRecord connectionRecord,
                                                    ReliableUdpHeader header, byte[] data)
 {
     connectionRecord.Tcb.PassMessageToSubscribers(new ReliableUdpMessage(header.ReliableUdpMessageType,
                                                                          data,
                                                                          header.Flags.HasFlag(
                                                                              ReliableUdpHeaderFlags.NoAsk)),
                                                   connectionRecord.RemoteClient);
 }
Exemplo n.º 5
0
 /// <summary>
 /// Gets byte array from ReliableUdpHeader
 /// </summary>
 /// <param name="header"></param>
 /// <returns></returns>
 internal static byte[] ToBytes(this ReliableUdpHeader header)
 {
     byte[] bytes = new byte[ReliableUdpHeader.Length];
     bytes[0] = (byte)header.Flags;
     Array.Copy(BitConverter.GetBytes((short)header.ReliableUdpMessageType), 0, bytes, 2, 2);
     Array.Copy(BitConverter.GetBytes(header.TransmissionId), 0, bytes, 4, 4);
     Array.Copy(BitConverter.GetBytes(header.PacketNumber), 0, bytes, 8, 4);
     Array.Copy(BitConverter.GetBytes(header.Options), 0, bytes, 12, 4);
     return(bytes);
 }
Exemplo n.º 6
0
        /// <summary>
        /// Sends acknowledge
        /// </summary>
        /// <exception cref="ArgumentNullException"></exception>
        /// <exception cref="ArgumentOutOfRangeException"></exception>
        /// <exception cref="ArgumentException"></exception>
        /// <exception cref="ObjectDisposedException"></exception>
        /// <exception cref="InvalidOperationException"></exception>
        /// <exception cref="SocketException"></exception>
        internal static void SendAcknowledgePacket(ReliableUdpConnectionRecord connectionRecord,
                                                   bool isCheckForLastOk = false)
        {
            //Debug.WriteLine( "To {0}. Ask {1} | connectionRecord.RcvCurrent {2} | connectionRecord.WLB {3}", connectionRecord.RemoteClient, connectionRecord.RcvCurrent + 1, connectionRecord.RcvCurrent, connectionRecord.WindowLowerBound );
            ReliableUdpHeader header = CreateAcknowledgeHeader(connectionRecord);

            if (isCheckForLastOk)
            {
                header.Flags = header.Flags | ReliableUdpHeaderFlags.LastPacket;
            }

            connectionRecord.Tcb.SendUdp(header.ToBytes(), connectionRecord.RemoteClient);
        }
Exemplo n.º 7
0
        /// <summary>
        /// Creates Reliable Udp header from connectionRecord
        /// </summary>
        /// <param name="connectionRecord">Current <see cref="ReliableUdpConnectionRecord"/> (ConnectionRecord)</param>
        /// <returns></returns>
        internal static ReliableUdpHeader CreateReliableUdpHeader(ReliableUdpConnectionRecord connectionRecord)
        {
            ReliableUdpHeader header = new ReliableUdpHeader();

            //RU: запись номера передачи
            header.TransmissionId = connectionRecord.TransmissionId;
            //RU: тип сообщения
            header.ReliableUdpMessageType = connectionRecord.ReliableUdpMessageType;
            //RU: установка номера пакета для передачи
            header.PacketNumber = connectionRecord.SndNext;

            //RU: запись управляющих флагов
            ReliableUdpHeaderFlags flags = ReliableUdpHeaderFlags.None;

            //RU: выставляем комбинацию двух флагов для единственного сообщения
            //EN: set flags for single packet message
            if (connectionRecord.NumberOfPackets == 1)
            {
                flags = ReliableUdpHeaderFlags.FirstPacket | ReliableUdpHeaderFlags.LastPacket;
            }
            //RU: дополнительный флаг если нам не требуется подтверждение
            //EN: set additional flag if we don't need to use acknowledge mechanism
            if (connectionRecord.IsNoAnswerNeeded)
            {
                flags = flags | ReliableUdpHeaderFlags.NoAsk;
            }
            //RU: это первый пакет
            //EN: check for first packet
            if (connectionRecord.NumberOfPackets > 1 && connectionRecord.SndNext == 0)
            {
                flags = flags | ReliableUdpHeaderFlags.FirstPacket;
            }
            //RU: это последний пакет
            //EN: check for last packet
            if (connectionRecord.NumberOfPackets > 1 &&
                (connectionRecord.NumberOfPackets - connectionRecord.SndNext - 1 == 0))
            {
                flags = flags | ReliableUdpHeaderFlags.LastPacket;
            }
            //RU: записываем флаги
            header.Flags = flags;

            //RU: запись поля Options - размер всего сообщения
            //EN: if we've got first message we should write total size of message
            //EN: in header.Options
            if (header.Flags.HasFlag(ReliableUdpHeaderFlags.FirstPacket))
            {
                header.Options = connectionRecord.OutcomingStream.Length;
            }
            return(header);
        }
Exemplo n.º 8
0
        internal static ReliableUdpHeader CreateReliableUdpHeader(Int32 transmissionId,
                                                                  ReliableUdpHeaderFlags flags,
                                                                  ReliableUdpMessageTypes reliableUdpMessageType,
                                                                  Int32 sequenceNumber, Int32 options)
        {
            ReliableUdpHeader header = new ReliableUdpHeader
            {
                TransmissionId         = transmissionId,
                Flags                  = flags,
                ReliableUdpMessageType = reliableUdpMessageType,
                PacketNumber           = sequenceNumber,
                Options                = options,
            };

            return(header);
        }
Exemplo n.º 9
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);
     }
 }
Exemplo n.º 10
0
 /// <summary>
 /// Writes packet data to byte array
 /// </summary>
 /// <param name="connectionRecord">Current <see cref="ReliableUdpConnectionRecord"/> (ConnectionRecord)</param>
 /// <param name="header"><see cref="ReliableUdpHeader"/></param>
 /// <param name="data">Udp payload</param>
 internal static void WritePacketData(ReliableUdpConnectionRecord connectionRecord, ReliableUdpHeader header,
                                  byte[] data)
 {
     //RU: Т.к. мы используем пакеты сторого фиксированного размера
       //RU: и при отправке разбитого на части Сообщения отправляем все пакеты,
       //RU: кроме последнего, будут одного размера, то спокойно можем использовать
       //RU: Header.PacketNumber* connectionRecord.BufferSize для указания позиции записи.
       //EN: We use fixed-size packets to send all packets except last one. So
       //EN: We can easily establish a position for data recording by this formula: Header.PacketNumber* connectionRecord.BufferSize
       Array.Copy(data, ReliableUdpHeader.Length, connectionRecord.IncomingStream,
          header.PacketNumber*connectionRecord.BufferSize, data.Length - ReliableUdpHeader.Length);
 }
Exemplo n.º 11
0
        /// <summary>
        /// Reads ReliableUdp header
        /// </summary>
        /// <param name="udpPayloadBytes">Payload of udp packet</param>
        /// <param name="header"><see cref="ReliableUdpHeader"/></param>
        /// <returns><c>false</c> if packet is garbage or damaged</returns>
        internal static bool ReadReliableUdpHeader(byte[] udpPayloadBytes,
                                               out ReliableUdpHeader header)
        {
            header = new ReliableUdpHeader();
              //RU: в случае поврежденного пакета
              //EN: in case of damaged packet
              if (udpPayloadBytes.Length < ReliableUdpHeader.Length)
            return false;

              header.Flags = (ReliableUdpHeaderFlags) udpPayloadBytes[0];
              header.ReliableUdpMessageType = (ReliableUdpMessageTypes) BitConverter.ToInt16(udpPayloadBytes, 2);
              header.TransmissionId = BitConverter.ToInt32(udpPayloadBytes, 4);
              header.PacketNumber = BitConverter.ToInt32(udpPayloadBytes, 8);

              //RU: здесь мы проверяем первый пакет из множества пакетов, передающих сообщение
              //RU: узнаем размер сообщения, размер нам необходим для создания MemoryStream'а
              //EN: here we check first packet with data and get total size of message from
              //EN: Options field
              if (header.Flags.HasFlag(ReliableUdpHeaderFlags.FirstPacket)
              && (!header.Flags.HasFlag(ReliableUdpHeaderFlags.RequestForPacket))
            )
              {
            header.Options = BitConverter.ToInt32(udpPayloadBytes, 12);

            if (header.Options < 1)
            {
              //EN: drop damaged packet
              return false;
            }
              }
              return true;
        }
Exemplo n.º 12
0
        /// <summary>
        /// Initialises new byte array to save message packets data
        /// </summary>
        /// <param name="connectionRecord">Current <see cref="ReliableUdpConnectionRecord"/> (ConnectionRecord)</param>
        /// <param name="header"><see cref="ReliableUdpHeader"/></param>   
        internal static void InitIncomingBytesStorage(ReliableUdpConnectionRecord connectionRecord, ReliableUdpHeader header)
        {
            //RU: Параметры проверены на этапе приема пакета и создания connectionRecord

              //RU: здесь я оставляю макисмально возможный размер массива доступным. Хотя
              //RU: вышестоящие протоколам не следует пользоваться этой возможностью без
              //RU: острой на то необходимости

              //RU: пояснение к Header.Options - для первого пакета (c флагом FirstPacket) мы указываем длину
              //RU: передаваемого сообщения в поле Header.Options

              //EN: all parameters are checked when we received packets and created connectionRecord.

              //EN: here I decided to allow maximum size of byte array. Although, the
              //EN: upper-level protocols should not use this feature without need

              //EN: about header.Options - in the first transmitting packet we set this field to size of message
              connectionRecord.IncomingStream = connectionRecord.Tcb.IncomingStreams.GetOrAdd(connectionRecord.Key,
                                                                                      new byte[header.Options]);
        }
Exemplo n.º 13
0
        /// <summary>
        /// Creates udp payload
        /// </summary>
        /// <param name="connectionRecord">Current <see cref="ReliableUdpConnectionRecord"/> (ConnectionRecord)</param>
        /// <param name="header"><see cref="ReliableUdpHeader"/></param>
        /// <returns>Array of bytes with header and data</returns>
        internal static byte[] CreateUdpPayload(ReliableUdpConnectionRecord connectionRecord, ReliableUdpHeader header)
        {
            //byte[] bytes = Header.Flags.HasFlag(ReliableUdpHeaderFlags.LastPacket)
              //  ? new byte[connectionRecord.OutcomingStream.Length - connectionRecord.RcvCurrent*connectionRecord.BufferSize]
              //  :new byte[connectionRecord.BufferSize];

              //Array.Copy(connectionRecord.OutcomingStream,connectionRecord.BufferSize * connectionRecord.SndCurrent,bytes,0,bytes.Length);

              //byte[] udpPayload = new byte[bytes.Length + ReliableUdpHeader.Length];
              //Array.Copy( Header.ToBytes(), udpPayload, ReliableUdpHeader.Length );
              //Array.Copy( bytes, 0, udpPayload, 0, bytes.Length );

              int length = header.Flags.HasFlag(ReliableUdpHeaderFlags.LastPacket)
                     ? connectionRecord.OutcomingStream.Length - connectionRecord.SndNext*connectionRecord.BufferSize
                     : connectionRecord.BufferSize;

              byte[] udpPayload = new byte[length + ReliableUdpHeader.Length];
              Array.Copy(header.ToBytes(), udpPayload, ReliableUdpHeader.Length);
              Array.Copy(connectionRecord.OutcomingStream, connectionRecord.BufferSize*connectionRecord.SndNext, udpPayload,
                 ReliableUdpHeader.Length, length);

              return udpPayload;
        }
Exemplo n.º 14
0
        /// <summary>
        /// Creates udp payload
        /// </summary>
        /// <param name="connectionRecord">Current <see cref="ReliableUdpConnectionRecord"/> (ConnectionRecord)</param>
        /// <param name="header"><see cref="ReliableUdpHeader"/></param>
        /// <returns>Array of bytes with header and data</returns>
        internal static byte[] CreateUdpPayload(ReliableUdpConnectionRecord connectionRecord, ReliableUdpHeader header)
        {
            //byte[] bytes = Header.Flags.HasFlag(ReliableUdpHeaderFlags.LastPacket)
            //  ? new byte[connectionRecord.OutcomingStream.Length - connectionRecord.RcvCurrent*connectionRecord.BufferSize]
            //  :new byte[connectionRecord.BufferSize];

            //Array.Copy(connectionRecord.OutcomingStream,connectionRecord.BufferSize * connectionRecord.SndCurrent,bytes,0,bytes.Length);

            //byte[] udpPayload = new byte[bytes.Length + ReliableUdpHeader.Length];
            //Array.Copy( Header.ToBytes(), udpPayload, ReliableUdpHeader.Length );
            //Array.Copy( bytes, 0, udpPayload, 0, bytes.Length );

            int length = header.Flags.HasFlag(ReliableUdpHeaderFlags.LastPacket)
                     ? connectionRecord.OutcomingStream.Length - connectionRecord.SndNext * connectionRecord.BufferSize
                     : connectionRecord.BufferSize;

            byte[] udpPayload = new byte[length + ReliableUdpHeader.Length];
            Array.Copy(header.ToBytes(), udpPayload, ReliableUdpHeader.Length);
            Array.Copy(connectionRecord.OutcomingStream, connectionRecord.BufferSize * connectionRecord.SndNext, udpPayload,
                       ReliableUdpHeader.Length, length);

            return(udpPayload);
        }
Exemplo n.º 15
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);
       }
 }
Exemplo n.º 16
0
 public virtual void ReceivePacket(ReliableUdpConnectionRecord connectionRecord, ReliableUdpHeader header,
                                   byte[] payload)
 {
 }
Exemplo n.º 17
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);
              }
        }
Exemplo n.º 18
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);
            }
        }
Exemplo n.º 19
0
 public virtual void ReceivePacket(ReliableUdpConnectionRecord connectionRecord, ReliableUdpHeader header,
     byte[] payload)
 {
 }
Exemplo n.º 20
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);
              }
        }
Exemplo n.º 21
0
        /// <summary>
        /// Initialises new byte array to save message packets data
        /// </summary>
        /// <param name="connectionRecord">Current <see cref="ReliableUdpConnectionRecord"/> (ConnectionRecord)</param>
        /// <param name="header"><see cref="ReliableUdpHeader"/></param>
        internal static void InitIncomingBytesStorage(ReliableUdpConnectionRecord connectionRecord, ReliableUdpHeader header)
        {
            //RU: Параметры проверены на этапе приема пакета и создания connectionRecord

            //RU: здесь я оставляю макисмально возможный размер массива доступным. Хотя
            //RU: вышестоящие протоколам не следует пользоваться этой возможностью без
            //RU: острой на то необходимости

            //RU: пояснение к Header.Options - для первого пакета (c флагом FirstPacket) мы указываем длину
            //RU: передаваемого сообщения в поле Header.Options

            //EN: all parameters are checked when we received packets and created connectionRecord.

            //EN: here I decided to allow maximum size of byte array. Although, the
            //EN: upper-level protocols should not use this feature without need

            //EN: about header.Options - in the first transmitting packet we set this field to size of message
            connectionRecord.IncomingStream = connectionRecord.Tcb.IncomingStreams.GetOrAdd(connectionRecord.Key,
                                                                                            new byte[header.Options]);
        }
Exemplo n.º 22
0
 /// <summary>
 /// Creates header of acknowledge packet
 /// </summary>
 /// <param name="connectionRecord">Current <see cref="ReliableUdpConnectionRecord"/></param>
 /// <param name="isAskForLost"><c>true</c> if we need resending</param>
 /// <param name="seqNum">Lost packet number if <paramref name="isAskForLost"/> is <c>true</c></param>
 /// <returns></returns>
 internal static ReliableUdpHeader CreateAcknowledgeHeader(ReliableUdpConnectionRecord connectionRecord,
                                                       bool isAskForLost = false, int seqNum = -1)
 {
     ReliableUdpHeader header = new ReliableUdpHeader();
       //RU: запись номера передачи
       header.TransmissionId = connectionRecord.TransmissionId;
       //RU: тип сообщения
       header.ReliableUdpMessageType = connectionRecord.ReliableUdpMessageType;
       //RU: установка Ask флаг
       //EN: set Ask flag
       header.Flags = ReliableUdpHeaderFlags.RequestForPacket;
       //RU: для первого пакета устанавливаем соответствующий флаг
       //EN: set FirstPacket flag for the ask on first packet
       if (connectionRecord.RcvCurrent == 0)
       {
     header.Flags = header.Flags | ReliableUdpHeaderFlags.FirstPacket;
       }
       if (!isAskForLost)
       {
     //RU: установка номера пакета для передачи - означает что ждем следующий пакет
     //EN: it means that we are ready and waiting for next block of data
     header.PacketNumber = connectionRecord.RcvCurrent + 1;
       }
       else
       {
     header.PacketNumber = seqNum;
       }
       return header;
 }
Exemplo n.º 23
0
 /// <summary>
 /// Creates message from single packet
 /// </summary>
 /// <param name="connectionRecord">Current <see cref="ReliableUdpConnectionRecord"/> (ConnectionRecord)</param>
 /// <param name="header"><see cref="ReliableUdpHeader"/></param>
 /// <param name="data">packet data (udp payload - header)</param>
 internal static void CreateMessageFromSinglePacket(ReliableUdpConnectionRecord connectionRecord,
                                                ReliableUdpHeader header, byte[] data)
 {
     connectionRecord.Tcb.PassMessageToSubscribers(new ReliableUdpMessage(header.ReliableUdpMessageType,
                                                                    data,
                                                                    header.Flags.HasFlag(
                                                                      ReliableUdpHeaderFlags.NoAsk)),
                                             connectionRecord.RemoteClient);
 }
Exemplo n.º 24
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);
            }
        }
Exemplo n.º 25
0
 internal static ReliableUdpHeader CreateReliableUdpHeader(Int32 transmissionId,
                                                       ReliableUdpHeaderFlags flags,
                                                       ReliableUdpMessageTypes reliableUdpMessageType,
                                                       Int32 sequenceNumber, Int32 options)
 {
     ReliableUdpHeader header = new ReliableUdpHeader
                            {
                              TransmissionId = transmissionId,
                              Flags = flags,
                              ReliableUdpMessageType = reliableUdpMessageType,
                              PacketNumber = sequenceNumber,
                              Options = options,
                            };
       return header;
 }
Exemplo n.º 26
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);
            }
        }
Exemplo n.º 27
0
        /// <summary>
        /// Creates Reliable Udp header from connectionRecord
        /// </summary>
        /// <param name="connectionRecord">Current <see cref="ReliableUdpConnectionRecord"/> (ConnectionRecord)</param>
        /// <returns></returns>
        internal static ReliableUdpHeader CreateReliableUdpHeader(ReliableUdpConnectionRecord connectionRecord)
        {
            ReliableUdpHeader header = new ReliableUdpHeader();
              //RU: запись номера передачи
              header.TransmissionId = connectionRecord.TransmissionId;
              //RU: тип сообщения
              header.ReliableUdpMessageType = connectionRecord.ReliableUdpMessageType;
              //RU: установка номера пакета для передачи
              header.PacketNumber = connectionRecord.SndNext;

              //RU: запись управляющих флагов
              ReliableUdpHeaderFlags flags = ReliableUdpHeaderFlags.None;
              //RU: выставляем комбинацию двух флагов для единственного сообщения
              //EN: set flags for single packet message
              if (connectionRecord.NumberOfPackets == 1)
              {
            flags = ReliableUdpHeaderFlags.FirstPacket | ReliableUdpHeaderFlags.LastPacket;
              }
              //RU: дополнительный флаг если нам не требуется подтверждение
              //EN: set additional flag if we don't need to use acknowledge mechanism
              if (connectionRecord.IsNoAnswerNeeded)
              {
            flags = flags | ReliableUdpHeaderFlags.NoAsk;
              }
              //RU: это первый пакет
              //EN: check for first packet
              if (connectionRecord.NumberOfPackets > 1 && connectionRecord.SndNext == 0)
              {
            flags = flags | ReliableUdpHeaderFlags.FirstPacket;
              }
              //RU: это последний пакет
              //EN: check for last packet
              if (connectionRecord.NumberOfPackets > 1
              && (connectionRecord.NumberOfPackets - connectionRecord.SndNext - 1 == 0))
              {
            flags = flags | ReliableUdpHeaderFlags.LastPacket;
              }
              //RU: записываем флаги
              header.Flags = flags;

              //RU: запись поля Options - размер всего сообщения
              //EN: if we've got first message we should write total size of message
              //EN: in header.Options
              if (header.Flags.HasFlag(ReliableUdpHeaderFlags.FirstPacket))
              {
            header.Options = connectionRecord.OutcomingStream.Length;
              }
              return header;
        }
Exemplo n.º 28
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);
            }
        }
Exemplo n.º 29
0
 /// <summary>
 /// Writes packet data to byte array
 /// </summary>
 /// <param name="connectionRecord">Current <see cref="ReliableUdpConnectionRecord"/> (ConnectionRecord)</param>
 /// <param name="header"><see cref="ReliableUdpHeader"/></param>
 /// <param name="data">Udp payload</param>
 internal static void WritePacketData(ReliableUdpConnectionRecord connectionRecord, ReliableUdpHeader header,
                                      byte[] data)
 {
     //RU: Т.к. мы используем пакеты сторого фиксированного размера
     //RU: и при отправке разбитого на части Сообщения отправляем все пакеты,
     //RU: кроме последнего, будут одного размера, то спокойно можем использовать
     //RU: Header.PacketNumber* connectionRecord.BufferSize для указания позиции записи.
     //EN: We use fixed-size packets to send all packets except last one. So
     //EN: We can easily establish a position for data recording by this formula: Header.PacketNumber* connectionRecord.BufferSize
     Array.Copy(data, ReliableUdpHeader.Length, connectionRecord.IncomingStream,
                header.PacketNumber * connectionRecord.BufferSize, data.Length - ReliableUdpHeader.Length);
 }
Exemplo n.º 30
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);
     }
 }
Exemplo n.º 31
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);
              }
        }