/// <summary> /// Sends byte[] to socket. /// Creates datagram with frame format given as example in demonstration example. /// Switches state to waitingforack. /// Set sendAsIs when sending/resending valid datagram/frame. /// </summary> /// <param name="data">Byte array to be sent over socket</param> /// <param name="sendAsIs">Boolen: true - datagram has been contructed before, false - construct new datagram and send it</param> protected virtual async void RdtSend(byte[] data, bool sendAsIs = false) { if (sendAsIs) { socket.Send(data); } else { switch (state) { case (int)STATE.WaitingCallFromAboveOrBelow: { byte[] newDatagram = rdt.MakeDatagram(data, senderSeq); socket.Send(newDatagram); previouslySentDatagram = newDatagram; state = (int)STATE.WaitingForAck; break; } case (int)STATE.WaitingForAck: { RaiseOnDeliver(InvokeReason.Error, "Waiting for ACK/NACK, add to queue."); messageBuffer.Add(data); 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 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"); } }