private void RdtReceive(byte[] datagram) { bool isOk = rdt.IsOk(datagram); string str = rdt.GetDatagramContentString(datagram); bool isAck = rdt.IsAck(datagram); bool isNack = rdt.IsNack(datagram); //Very naive implementation! int seqDatagram = rdt.GetSeqNum(datagram); /*We are sender*/ if (state == (int)STATE.WaitingForAck) { if (isOk && isAck && seqDatagram == senderSeq) { if (messageBuffer.Count > 0) { senderSeq = rdt.Incmod(senderSeq, flipBit); //toggle seq bit by using window of 2. 0->1 || 1->0 RaiseOnDeliver(InvokeReason.Debug, "Sending 1st queued message"); ToggleState(); RdtSend(messageBuffer[0]); messageBuffer.RemoveAt(0); } else { ToggleState(); senderSeq = rdt.Incmod(senderSeq, flipBit); //toggle seq bit by using window of 2. 0->1 || 1->0 RaiseOnDeliver(InvokeReason.Debug, "Received correct ACK, switching states to #" + state.ToString()); } } else { RdtSend(previouslySentDatagram, true); RaiseOnDeliver(InvokeReason.Debug, "Sender got received corrupted or out of order packet. Resending"); } } /*We are receiver*/ else { if (!isOk) { //Corrupt RdtSend(rdt.MakeAck(receiverSeq, true), true); RaiseOnDeliver(InvokeReason.Debug, "Received corrupted, sending NACK"); } else if (isOk && receiverSeq != seqDatagram) { //Unexpected seqnum RdtSend(rdt.MakeAck(rdt.Incmod(receiverSeq, flipBit)), true); RaiseOnDeliver(InvokeReason.Debug, "Received out-of-order, sending ACK #" + receiverSeq.ToString()); } else { //Received new message RaiseOnDeliver(InvokeReason.Receiver, str); receiverSeq = rdt.Incmod(receiverSeq, flipBit); RdtSend(rdt.MakeAck(seqDatagram), true); } } }
/*This implementation has flaw where gbnWindow doesn't mean window per se, * more like whole seqnum space...Will do proper way with selective repeat.*/ public GoBackN(VirtualSocket socket) { rdt = new RdtUtils(); this.socket = socket; socket.Start(); socket.OnReceive += RdtReceive; gbnSendDictionary = new Dictionary <int, byte[]>(); gbnLatestAck = rdt.MakeAck(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); } } }
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"); } }