private bool TrySendDatagram(Datagram datagram, bool hasAlreadyBeenDelayed = false) { // If we are the keep alive master (i.e. this remote peer is not) and this // packet is reliable: update ellpasedMilliseondsAtLastRealiablePacket[Sent] if (!IsKeepAliveMaster && datagram.IsReliable) { ellapasedSecondsSinceLastRealiablePacket = 0.0f; } // simulate packet loss if (localPeer.SimulatePacketLossProbability > 0.0) { if (SingleRandom.NextDouble() < localPeer.SimulatePacketLossProbability) { localPeer.Log(LogLevel.Info, String.Format("DROPPED packet to send - simulate packet loss set at: {0}", localPeer.SimulatePacketLossChance)); return(true); } } // if reliable and not already delayed update time sent used to measure RTT when ACK response received if (datagram.IsReliable && !hasAlreadyBeenDelayed) { datagram.EllapsedAtSent = localPeer.Stopwatch.Elapsed; } // simulate delay if (localPeer.SimulateLatencySeconds > 0.0f && !hasAlreadyBeenDelayed && datagram.Type != PacketType.Bye) // if Bye we don't delay as if closing will not be sent { float delay = localPeer.SimulateLatencySeconds; // jitter if (localPeer.SimulateJitterSeconds > 0.0f) { float jitter = localPeer.SimulateJitterSeconds * (float)SingleRandom.NextDouble(); if (SingleRandom.NextDouble() < 0.5) { jitter *= -1; } delay += jitter; } DelayedDatagram delayedDatagram = new DelayedDatagram(delay, datagram); localPeer.Log(LogLevel.Debug, String.Format("...DELAYED Sending datagram to: {0}, seq {1}, channel: {2}, total size: {3}; by {4}s...", endPoint.ToString(), datagram.Sequence.ToString(), datagram.SendOptions.ToString(), datagram.Count.ToString(), delayedDatagram.EllapsedSecondsRemainingToDelay.ToString())); delayedDatagrams.Add(delayedDatagram); return(true); } localPeer.Log(LogLevel.Debug, String.Format("--> Sending datagram to: {0}, seq {1}, channel: {2}, total size: {3}...", endPoint.ToString(), datagram.Sequence.ToString(), datagram.SendOptions.ToString(), datagram.Count.ToString())); // Expedite send if less than 5% of recent reliable packets have to be re-sent bool expedite = qualityOfService.ResendRatio < 0.05f; //----------------------------------------------------------------------------------------------------------------- bool success = localPeer.Transceiver.Send(datagram.BackingBuffer, datagram.Offset, datagram.Count, endPoint, expedite); //----------------------------------------------------------------------------------------------------------------- if (success) { if (localPeer.IsCollectingStatistics) { localPeer.Statistics.AddBytesSent(datagram.Count); } // return the datagram to pool for re-use if we are not waiting for an ACK if (!datagram.IsReliable) { sendDatagramsPool.Return(datagram); } } else { // something fatal has gone wrong and this peer can no longer be sent to localPeer.RemovePeerOnNextUpdate(this); } return(success); }
internal void Update(float dt) { // // Update counters // ellapasedSecondsSinceLastRealiablePacket += dt; ellapsedSecondsSinceSendQueuesLastFlushed += dt; //// //// Update enqued ACKs stopover time //// //if (enqueudAcks.Count > 0) //{ // for(int i = 0; i <enqueudAcks.Count; i++) // { // AckDetail oldAck = enqueudAcks[i]; // AckDetail newAck = new AckDetail(); // newAck.Seq = oldAck.Seq; // newAck.Channel = oldAck.Channel; // newAck.EllapsedSecondsSinceEnqueued = oldAck.EllapsedSecondsSinceEnqueued + dt; // enqueudAcks[i] = newAck; // } //} // // Datagrams awaiting ACKs // if (sentDatagramsAwaitingACK.Count > 0) { bool anyResendsAwaitingACK = false; for (int i = 0; i < sentDatagramsAwaitingACK.Count; i++) { Datagram datagramAwaitingAck = sentDatagramsAwaitingACK[i]; datagramAwaitingAck.EllapsedSecondsSincePacketSent += dt; float ackTimeout = localPeer.GetAckTimeout(datagramAwaitingAck.ResentCount); if (datagramAwaitingAck.EllapsedSecondsSincePacketSent >= ackTimeout) { datagramAwaitingAck.ResentCount++; if (datagramAwaitingAck.ResentCount > localPeer.MaxResends) { // give-up, assume the peer has disconnected (without saying bye) and drop it sentDatagramsAwaitingACK.RemoveAt(i); i--; localPeer.Log(LogLevel.Warning, String.Format("Peer failed to ACK {0} re-sends of Reliable datagram seq {1}, channel {2}. total size: {3}, in time.", localPeer.MaxResends, datagramAwaitingAck.Sequence.ToString(), datagramAwaitingAck.SendOptions.ToString(), datagramAwaitingAck.Count.ToString())); sendDatagramsPool.Return(datagramAwaitingAck); localPeer.RemovePeerOnNextUpdate(this); return; } else { // try again.. datagramAwaitingAck.EllapsedSecondsSincePacketSent = 0.0f; ReSend(datagramAwaitingAck); ellapasedSecondsSinceLastRealiablePacket = 0.0f; // NOTE: this is reset again when packet actually sent but another Update() may occur before then localPeer.Log(LogLevel.Info, String.Format("Datagram to: {0}, seq {1}, channel: {2}, total size: {3} re-sent as not ACKnowledged in {4}s.", PeerName, datagramAwaitingAck.Sequence.ToString(), datagramAwaitingAck.SendOptions.ToString(), datagramAwaitingAck.Count.ToString(), ackTimeout.ToString())); } } if (datagramAwaitingAck.IsResend) { anyResendsAwaitingACK = true; } } qualityOfService.IsNotResponding = anyResendsAwaitingACK; } // // KeepAlives and AutoFlush // if (keepAliveAndAutoFlush) { if (IsKeepAliveMaster) // i.e. this remote peer is the keep alive master, not us { if (ellapasedSecondsSinceLastRealiablePacket >= localPeer.KeepAliveProbeAfterSeconds) { // This remote peer has not sent a reliable message for too long, send a // KeepAlive to probe them and they are alive! reliableSendChannel.EnqueueSend(PacketType.KeepAlive, null); ellapasedSecondsSinceLastRealiablePacket = 0.0f; } } else if (ellapasedSecondsSinceLastRealiablePacket >= localPeer.KeepAliveIntervalSeconds) { reliableSendChannel.EnqueueSend(PacketType.KeepAlive, null); ellapasedSecondsSinceLastRealiablePacket = 0.0f; // NOTE: this is reset again when packet actually sent but another Update() may occur before then } if (localPeer.AutoFlushIntervalSeconds > 0.0f) { if (ellapsedSecondsSinceSendQueuesLastFlushed >= localPeer.AutoFlushIntervalSeconds) { localPeer.Log(LogLevel.Info, "AutoFlush"); bool success = TryFlushSendQueues(); // resets ellapsedSecondsSinceSendQueuesLastFlushed if (!success) { return; } } } } // // Simulate Delay // if (delayedDatagrams.Count > 0) { for (int i = 0; i < delayedDatagrams.Count; i++) { DelayedDatagram delayedDatagram = delayedDatagrams[i]; delayedDatagram.EllapsedSecondsRemainingToDelay -= dt; if (delayedDatagram.EllapsedSecondsRemainingToDelay <= 0.0f) { bool success = TrySendDatagram(delayedDatagram.Datagram, true); if (!success) { return; } delayedDatagrams.RemoveAt(i); i--; } } } }