/*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; } } } } }
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."); } } }
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 } }
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."); } }
public void CancelPending() { cts.Cancel(); if (cts.IsCancellationRequested) { OnDeliver?.Invoke(InvokeReason.Sender, "[Canceled pending transmission(s)]"); } }
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"); }
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); } } }
public void RdtSend(string message) { RdtSend(Encoding.UTF8.GetBytes(message), false); OnDeliver?.Invoke(InvokeReason.Sender, message); }
protected void RaiseOnDeliver(InvokeReason reason, string text) { OnDeliver?.Invoke(reason, text); }
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"); } }
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; }