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); } } }
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"); } }