Ejemplo n.º 1
0
 // writes as many enqued as as can fit into datagram
 private void WriteEnquedAcksToDatagram(Datagram datagram, int index)
 {
     while (enqueudAcks.Count > 0 && (datagram.MaxSize - (index - datagram.Offset)) > Const.FALCON_PACKET_HEADER_SIZE)
     {
         AckDetail ack = enqueudAcks.Dequeue();
         FalconHelper.WriteAck(ack, datagram.BackingBuffer, index);
         index += Const.FALCON_PACKET_HEADER_SIZE;
     }
     datagram.Resize(index - datagram.Offset);
 }
Ejemplo n.º 2
0
        internal void EnqueueSend(PacketType type, Packet packet)
        {
            // NOTE: packet may be null in the case of Falcon system messages.

            if (packet != null && packet.BytesWritten > FalconPeer.MaxPayloadSize)
            {
                throw new InvalidOperationException(String.Format("Packet size: {0}, greater than max: {1}", packet.BytesWritten, FalconPeer.MaxPayloadSize));
            }

            bool isFalconHeaderWritten = currentDatagramTotalBufferOffset > currentDatagram.Offset;

            if (isFalconHeaderWritten)
            {
                if (packet != null && (packet.BytesWritten + Const.ADDITIONAL_PACKET_HEADER_SIZE) > (currentDatagram.Count - (currentDatagramTotalBufferOffset - currentDatagram.Offset))) // i.e. cannot fit
                {
                    // enqueue the current args and get a new one
                    EnqueueCurrentDatagram();
                    isFalconHeaderWritten = false;
                }
            }

            if (!isFalconHeaderWritten)
            {
                // write the falcon header
                FalconHelper.WriteFalconHeader(currentDatagram.BackingBuffer,
                                               currentDatagram.Offset,
                                               type,
                                               channelType,
                                               seqCount,
                                               packet == null ? (ushort)0 : (ushort)packet.BytesWritten);
                currentDatagramTotalBufferOffset += Const.FALCON_PACKET_HEADER_SIZE;
            }
            else
            {
                // TODO limit max additional to 100 so receive channel can distinguish ordinal seq

                // write additional header
                FalconHelper.WriteAdditionalFalconHeader(currentDatagram.BackingBuffer,
                                                         currentDatagramTotalBufferOffset,
                                                         type,
                                                         channelType,
                                                         packet == null ? (ushort)0 : (ushort)packet.BytesWritten);
                currentDatagramTotalBufferOffset += Const.ADDITIONAL_PACKET_HEADER_SIZE;
            }

            if (packet != null)
            {
                //---------------------------------------------------------------------------------------------------
                packet.CopyBytes(0, currentDatagram.BackingBuffer, currentDatagramTotalBufferOffset, packet.BytesWritten);
                //---------------------------------------------------------------------------------------------------

                currentDatagramTotalBufferOffset += packet.BytesWritten;
            }
        }
Ejemplo n.º 3
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);
            }
            }
        }