Esempio n. 1
0
 // used when datagram already constructed, e.g. re-sending unACKnowledged datagram
 internal void EnqueueSend(Datagram datagram)
 {
     queue.Enqueue(datagram);
 }
Esempio n. 2
0
        // returns true if caller should continue adding any additional packets in datagram
        internal bool TryAddReceivedPacket(
            ushort seq,
            SendOptions opts,
            PacketType type,
            byte[]      buffer,
            int index,
            int payloadSize,
            bool isFirstPacketInDatagram)
        {
            // If we are not the keep alive master, i.e. this remote peer is, and this packet was
            // sent reliably reset ellpasedMilliseondsAtLastRealiablePacket[Received].
            if (IsKeepAliveMaster && ((opts & SendOptions.Reliable) == SendOptions.Reliable))
            {
                ellapasedSecondsSinceLastRealiablePacket = 0.0f;
            }

            switch (type)
            {
            case PacketType.Application:
            case PacketType.KeepAlive:
            case PacketType.AcceptJoin:
            {
                bool wasAppPacketAdded;

                ReceiveChannel channel = noneReceiveChannel;
                switch (opts)
                {
                case SendOptions.InOrder:           channel = inOrderReceiveChannel;            break;

                case SendOptions.Reliable:          channel = reliableReceiveChannel;           break;

                case SendOptions.ReliableInOrder:   channel = reliableInOrderReceiveChannel;    break;
                }

                if (!channel.TryAddReceivedPacket(seq, type, buffer, index, payloadSize, isFirstPacketInDatagram, out wasAppPacketAdded))
                {
                    return(false);
                }

                if (wasAppPacketAdded)
                {
                    unreadPacketCount++;
                }

                return(true);
            }

            case PacketType.ACK:
            {
                // Look for the oldest datagram with the same seq AND channel type
                // seq is for which we ASSUME the ACK is for.
                int      datagramIndex;
                Datagram sentDatagramAwaitingAck = null;
                for (datagramIndex = 0; datagramIndex < sentDatagramsAwaitingACK.Count; datagramIndex++)
                {
                    Datagram datagram = sentDatagramsAwaitingACK[datagramIndex];
                    if (datagram.Sequence == seq && datagram.SendOptions == opts)
                    {
                        sentDatagramAwaitingAck = datagram;
                        break;
                    }
                }

                if (sentDatagramAwaitingAck == null)
                {
                    // Possible reasons:
                    // 1) ACK has arrived too late and the datagram must have already been removed.
                    // 2) ACK duplicated and has already been processed
                    // 3) ACK was unsolicited (i.e. malicious or buggy peer)

                    // NOTE: If ACK "piggy-backed" on a reliable datagram that was re-sent
                    //       that datagram if recieved more than once would have been dropped
                    //       when processing the first Application packet so should never
                    //       get to here.

                    localPeer.Log(LogLevel.Warning, "Datagram for ACK not found - too late?");
                }
                else
                {
                    // recieving our first ACK from this peer means they have Accepted us
                    if (!hasAccepted)
                    {
                        hasAccepted = true;
                    }

                    // remove datagram
                    sentDatagramsAwaitingACK.RemoveAt(datagramIndex);

                    // Update QoS
                    // ----------
                    //
                    // If the datagram was not re-sent update latency, otherwise we do not
                    // know which send this ACK is for so cannot determine latency.
                    //
                    // Also update re-sends sample if datagram was not re-sent with: not
                    // re-sent. If was re-sent sample would have already been updated when
                    // re-sent.

                    if (sentDatagramAwaitingAck.ResentCount == 0)
                    {
                        TimeSpan rtt = localPeer.Stopwatch.Elapsed - sentDatagramAwaitingAck.EllapsedAtSent;
                        qualityOfService.UpdateLatency(rtt);
                        qualityOfService.UpdateResentSample(false);

                        localPeer.Log(LogLevel.Debug, String.Format("ACK from: {0}, channel: {1}, seq: {2}, RTT: {3}s", PeerName, opts.ToString(), seq.ToString(), rtt.TotalSeconds.ToString()));
                        localPeer.Log(LogLevel.Debug, String.Format("Latency now: {0}s", qualityOfService.RoudTripTime.TotalSeconds.ToString()));
                    }
                    else
                    {
                        localPeer.Log(LogLevel.Info, String.Format("ACK for re-sent datagram: {0}, channel: {1}, seq: {2}", PeerName, opts.ToString(), seq.ToString()));
                    }


                    // return datagram
                    sendDatagramsPool.Return(sentDatagramAwaitingAck);
                }

                return(true);
            }

            case PacketType.Ping:
            {
                return(Pong());
            }

            case PacketType.Pong:
            {
                if (localPeer.HasPingsAwaitingPong)
                {
                    PingDetail detail = localPeer.PingsAwaitingPong.Find(pd => pd.PeerIdPingSentTo == Id);
                    if (detail != null)
                    {
                        localPeer.RaisePongReceived(this, TimeSpan.FromSeconds(detail.EllapsedSeconds));
                        localPeer.RemovePingAwaitingPongDetail(detail);
                    }
                }

                return(true);
            }

            case PacketType.JoinRequest:
            {
                if (hasAccepted)
                {
                    // If peer has accepted must be is joining again (possible when did not
                    // say Bye and has restarted again before timed out). Drop this
                    // instance of peer and process Accept next update so both peers on
                    // the same page, i.e. that we are starting a new connection.
                    //
                    byte[] datagram = new byte[Const.FALCON_PACKET_HEADER_SIZE + payloadSize];
                    FalconHelper.WriteFalconHeader(datagram, 0, PacketType.JoinRequest, SendOptions.None, (ushort)seq, (ushort)payloadSize);
                    if (payloadSize > 0)
                    {
                        Buffer.BlockCopy(buffer, index, datagram, Const.FALCON_PACKET_HEADER_SIZE, payloadSize);
                    }
                    localPeer.RemovePeerOnNextUpdate(this);
                    localPeer.EnqueuePacketToProcessOnNextUpdate(this.EndPoint, datagram);
                    return(false);
                }
                return(Accept());
            }

            case PacketType.DiscoverRequest:
            {
                DiscoverReply();
                return(true);
            }

            case PacketType.DiscoverReply:
            {
                // do nothing, DiscoveryReply only relevant when peer not added
                return(true);
            }

            case PacketType.Bye:
            {
                localPeer.Log(LogLevel.Info, String.Format("Bye received from: {0}.", PeerName));
                localPeer.RemovePeerOnNextUpdate(this);
                return(false);
            }

            default:
            {
                localPeer.Log(LogLevel.Error, String.Format("Datagram dropped - unexpected type: {0}, received from authenticated peer: {1}.", type, PeerName));
                return(false);
            }
            }
        }
Esempio n. 3
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);
        }
Esempio n. 4
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--;
                    }
                }
            }
        }