/// <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); }
/// <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); }
/// <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); }
/// <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); }
/// <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); }
/// <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); }
/// <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); }
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); }
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); } }
/// <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); }
/// <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; }
/// <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]); }
/// <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; }
/// <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); }
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); } }
public virtual void ReceivePacket(ReliableUdpConnectionRecord connectionRecord, ReliableUdpHeader header, byte[] payload) { }
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); } }
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); } }
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); } }
/// <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; }
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); } }
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; }
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); } }
/// <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; }
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); } }
/// <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); }
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); } }