Exemple #1
0
        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);
        }
Exemple #2
0
        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--;
                    }
                }
            }
        }