예제 #1
0
        /*Lightweight single timer to simulate multiple logical timers with timestamps and intervals*/
        private async Task SRTimer(CancellationToken token)
        {
            while (!token.IsCancellationRequested)
            {
                await Task.Delay(1000, token);

                //Below is probably problematic when seqnum space overflows...
                if (sendBucket.HasPending())
                {
                    for (byte i = srSendBase; i < (byte)(srSendBase + srWindowSize); i++)
                    {
                        if (sendBucket.ContainsKey(i) && sendBucket.IsSent(i) && !sendBucket.IsAcked(i))
                        {
                            if (DateTime.Now - sendBucket.GetTimestamp(i) > TimeSpan.FromMilliseconds(srTimeoutInterval))
                            {
                                RdtSend(sendBucket.GetDatagramBySeq(i), true);
                                sendBucket.SetTimestamp(i);
                                OnDeliver?.Invoke(InvokeReason.Debug, "Resent timedout pkt# " + i.ToString());
                            }
                        }
                        else
                        {
                            break;
                        }
                    }
                }
            }
        }
예제 #2
0
        private async void RdtSend(byte[] data, bool isOldDatagram = false)
        {
            if (isOldDatagram)
            {
                /*data has been sent before so it has seq nums etc*/
                socket.Send(data);
            }
            else
            {
                /*data is just new content as byte[] so new datagram is needed*/
                /*nextseqnum<base+N will fail when seqnum range overflows*/
                if (rdt.Distmod(gbnBase, gbnNextSeqNum, gbnWindowSize) < gbnWindowSize - 1)
                {
                    try
                    {
                        OnDeliver?.Invoke(InvokeReason.Debug, "4a" + PrintGbnDebug());

                        byte[] newDatagram = rdt.MakeDatagram(data, gbnNextSeqNum);
                        gbnSendDictionary[gbnNextSeqNum] = newDatagram;

                        socket.Send(gbnSendDictionary[gbnNextSeqNum]);
                        //simplify with pre-increment and combine following operations
                        gbnNextSeqNum = gbnNextSeqNum + 1;
                        gbnNextSeqNum = gbnNextSeqNum % gbnWindowSize;

                        OnDeliver?.Invoke(InvokeReason.Debug, "4b" + PrintGbnDebug());

                        if (cts != null)
                        {
                            cts.Cancel();
                        }
                        /*All standing packets are ack'd*/
                        if (gbnBase == gbnNextSeqNum)
                        {
                            //OnDeliver?.Invoke(InvokeReason.Debug, "Sending window is empty.");
                        }
                        else
                        {
                            //start new timer
                            //TODO: Check if this leaks resources
                            CancellationTokenSource aCTS = new CancellationTokenSource();
                            cts = aCTS;
                            await Timertimeout(aCTS.Token);

                            /*
                             * if (cts == aCTS)
                             * {
                             *  cts = null;
                             * }
                             */
                        }
                    }
                    catch (OperationCanceledException) { }
                }
                else //send window is full
                {
                    OnDeliver?.Invoke(InvokeReason.Error, "Sending window is full, try again later.");
                }
            }
        }
예제 #3
0
        private async void RdtSend(byte[] data, bool isOldDatagram = false)
        {
            if (isOldDatagram)
            {
                socket.Send(data);
            }
            else
            {
                byte[] datagram = rdt.MakeDatagram(data, srNextSeqNum);
                sendBucket.AddToBucket(datagram, srNextSeqNum);
                if (rdt.IsSeqInRange(srNextSeqNum, srSendBase, (byte)(srSendBase + srWindowSize - 1)))
                {
                    //Send via socket
                    socket.Send(datagram);
                    sendBucket.MarkAsSent(srNextSeqNum);
                    //start timer for this packet or set timestamp to simulate via single timer

                    sendBucket.SetTimestamp(srNextSeqNum);


                    OnDeliver?.Invoke(InvokeReason.Debug, "Message sent" + PrintSRDebugInfo());
                }
                else
                {
                    OnDeliver?.Invoke(InvokeReason.Debug, "Message Buffered" + PrintSRDebugInfo());
                }
                srNextSeqNum++; //pre-increment is more efficient here? edit: compiler optimization -> no difference
            }
        }
예제 #4
0
        public async void Receive()
        {
            try
            {
                UdpReceiveResult receivedResults;
                while (listen)
                {
                    receivedResults = await socket.ReceiveAsync();

                    var data = receivedResults.Buffer;
                    if (rnd.Next(100) >= Config.packetDropProp)
                    {
                        if (rnd.Next(101) < Config.bitErrorProp)
                        {
                            data = InsertBitError(data);
                            //textBox_Server.AppendText("[] VS inserted bit error []" + Environment.NewLine);
                            OnDeliver?.Invoke(InvokeReason.Debug, "Receving socket mutilated a datagram");
                        }
                        await Task.Delay(rnd.Next(Config.delayAmount * 1000));

                        OnReceive?.Invoke(data);
                    }
                    else
                    {
                        OnDeliver?.Invoke(InvokeReason.Debug, "Receving socket dropped a datagram");
                    }
                }
            }
            catch (ObjectDisposedException)
            {
                /*Short search revealed no easy way to cancel
                 * on going ReceiveAsync other than to catch this exception...*/
                MessageBox.Show("Closed socket.");
            }
        }
예제 #5
0
 public void CancelPending()
 {
     cts.Cancel();
     if (cts.IsCancellationRequested)
     {
         OnDeliver?.Invoke(InvokeReason.Sender, "[Canceled pending transmission(s)]");
     }
 }
예제 #6
0
        private async Task Timertimeout(CancellationToken token)
        {
            while (!token.IsCancellationRequested)
            {
                await Task.Delay(gbnTimeoutIntervalMs, token);

                if (token.IsCancellationRequested)
                {
                    break;
                }
                OnDeliver?.Invoke(InvokeReason.Debug, "Timer timeout");
                int i = gbnBase;
                while (i != gbnNextSeqNum)
                {
                    RdtSend(gbnSendDictionary[i], true);
                    i = (i + 1) % gbnWindowSize;
                }
            }
            //Timer cancelation has been requested
            OnDeliver?.Invoke(InvokeReason.Debug, "Timer is canceled");
        }
예제 #7
0
        private void RdtReceive(byte[] datagram)
        {
            bool   isOk   = rdt.IsOk(datagram);
            string str    = rdt.GetDatagramContentString(datagram);
            bool   isAck  = rdt.IsAck(datagram);
            int    seqNum = rdt.GetSeqNum(datagram);

            if (isOk)
            {
                if (isAck)
                {
                    try
                    {
                        if (gbnBase != gbnNextSeqNum)
                        {
                            gbnSendDictionary.Remove(seqNum);
                            gbnBase = seqNum + 1;
                            gbnBase = gbnBase % gbnWindowSize;
                            //timer is running so stop/cancel

                            /*All standing packets are ack'd*/
                            if (gbnBase == gbnNextSeqNum)
                            {
                                OnDeliver?.Invoke(InvokeReason.Debug, "Has sent all pending messages successfully" + PrintGbnDebug());
                                if (cts != null)
                                {
                                    cts.Cancel();
                                }
                            }
                            else
                            {
                                OnDeliver?.Invoke(InvokeReason.Debug, "2" + PrintGbnDebug());
                                //start new timer

                                /*CancellationTokenSource aCTS = new CancellationTokenSource();
                                 * cts = aCTS;
                                 * await Timertimeout(aCTS.Token);
                                 * if (cts == aCTS)
                                 * {
                                 *  cts = null;
                                 * }*/
                            }
                        }
                    }
                    catch (OperationCanceledException) { }
                }
                else
                {
                    /*not ack, not corrupt*/
                    if (seqNum == gbnExpectedSeqNum)
                    {
                        gbnLatestAck = rdt.MakeAck(gbnExpectedSeqNum);
                        RdtSend(gbnLatestAck, true);
                        gbnExpectedSeqNum++;
                        gbnExpectedSeqNum = gbnExpectedSeqNum % gbnWindowSize;
                        //If there is a subscriber attached to OnDeliver (e.g. UI), invoke event.
                        OnDeliver?.Invoke(InvokeReason.Receiver, str);
                        OnDeliver?.Invoke(InvokeReason.Debug, "3" + PrintGbnDebug());
                    }
                    else
                    {
                        /*As in not waiting for any acks, ie is receiver*/
                        if (gbnSendDictionary.Count == 0)
                        {
                            OnDeliver?.Invoke(InvokeReason.Debug, "Unexpected ACK SEQ (lost packets or duplicates?), resending ACK for datagram: "
                                              + rdt.GetSeqNum(gbnLatestAck).ToString());
                            RdtSend(gbnLatestAck, true);
                        }
                    }
                }
            }
            else
            {
                /*corrupt
                 * Assuming we are receiver e.g. no outstanding acks
                 */
                if (gbnSendDictionary.Count == 0)
                {
                    OnDeliver?.Invoke(InvokeReason.Debug, "Corrupted datagram. Resending ACK with seq: "
                                      + rdt.GetSeqNum(gbnLatestAck).ToString());
                    RdtSend(gbnLatestAck, true);
                }
            }
        }
예제 #8
0
 public void RdtSend(string message)
 {
     RdtSend(Encoding.UTF8.GetBytes(message), false);
     OnDeliver?.Invoke(InvokeReason.Sender, message);
 }
예제 #9
0
 protected void RaiseOnDeliver(InvokeReason reason, string text)
 {
     OnDeliver?.Invoke(reason, text);
 }
예제 #10
0
        private void RdtReceive(byte[] datagram)
        {
            bool isOk = rdt.IsOk(datagram);

            if (isOk)
            {
                string str    = rdt.GetDatagramContentString(datagram);
                bool   isAck  = rdt.IsAck(datagram);
                byte   seqNum = (byte)rdt.GetSeqNum(datagram);

                if (isAck)
                {
                    //Mark as ack'd
                    //If seq == sendBase => move window by:
                    //Go through buffer and if packets are in window and pkt.sent = false => send it and toggle bool

                    if (rdt.IsSeqInRange(seqNum, srSendBase, (byte)(srSendBase + srWindowSize - 1)))
                    {
                        sendBucket.MarkAsAcked(seqNum);
                        OnDeliver?.Invoke(InvokeReason.Debug, "Received ACK: " + seqNum.ToString()
                                          + PrintSRDebugInfo());
                        if (seqNum == srSendBase)
                        {
                            //int i = 0;
                            //While packet is sent -> remove it from sendBucket, increment counter, add counter to srSendBase
                            while (sendBucket.IsSent(srSendBase) && sendBucket.IsAcked(srSendBase))
                            {
                                sendBucket.RemoveFromBucket(srSendBase);
                                srSendBase++;
                            }
                            //While packet in sendBucket is in new window && hasn't been sent, send it and toggle it's state
                            //E.g for each srSendBase to srSendBase + N -1, if is in sendBucket & is not sent, send it now
                            //As dictionary is not ordered, we loop over range srSendBase -> srSendBase + N - 1. Stopping early if key isn't in bucket.
                            for (byte i = srSendBase; rdt.IsSeqInRange(i, srSendBase, (byte)(srSendBase + srWindowSize - 1)); i++)
                            {
                                if (!sendBucket.ContainsKey(i))
                                {
                                    break;
                                }
                                socket.Send(sendBucket.GetDatagramBySeq(i));
                                sendBucket.MarkAsSent(i);
                                //srNextSeqNum++;
                            }
                            OnDeliver.Invoke(InvokeReason.Debug, "Sender window moved. " + PrintSRDebugInfo());
                        }
                    }
                }
                else
                {
                    if (rdt.IsSeqInRange(seqNum, (byte)(srRcvBase - srWindowSize), (byte)(srRcvBase + srWindowSize - 1)))
                    {
                        RdtSend(rdt.MakeAck(seqNum), true);
                        OnDeliver?.Invoke(InvokeReason.Debug, "Received Pkt: " + seqNum.ToString()
                                          + PrintSRDebugInfo());
                        if (rdt.IsSeqInRange(seqNum, srRcvBase, (byte)(srRcvBase + srWindowSize - 1)))
                        {
                            if (!rcvBucket.ContainsKey(seqNum))
                            {
                                rcvBucket.AddToBucket(datagram, seqNum);
                            }
                            else
                            {
                                OnDeliver.Invoke(InvokeReason.Debug, "Received duplicate: " + seqNum.ToString());
                            }

                            if (seqNum == srRcvBase)
                            {
                                /*Pass a Action delegate for handling method. Method chain removes from buffer,
                                 * handles packets, returns count of handled. Naming scheme I use is bad, I know :(*/
                                srRcvBase = (byte)(srRcvBase + ProcessValidBufferedSequence(srRcvBase,
                                                                                            DeliverDataSequenceToUpperLayer));
                                OnDeliver.Invoke(InvokeReason.Debug, "Receiver window moved. " + PrintSRDebugInfo());
                            }
                        }
                    }
                }
            }
            else
            {
                //This Kurose implementation will ignore corrupted packets and relies on timeout to recover
                //Alternative is to send SREPEAT-control message to request retransmission for seqnum-1 packet.

                OnDeliver?.Invoke(InvokeReason.Error, "Received corrupted");
            }
        }
예제 #11
0
 private void DeliverDataSequenceToUpperLayer(List <byte[]> items)
 {
     items.ForEach((i) => {
         OnDeliver?.Invoke(InvokeReason.Receiver, rdt.GetDatagramContentString(i));
     });
 }
 void IBroadcastEvent.Deliver()
 {
     Assert.IsFalse(isDelivered, $"{this} has already been delivered!");
     OnDeliver?.Invoke((T)this);
     isDelivered = true;
 }