Exemple #1
0
        public bool EnqueueOutgoing(OutgoingPacket packet)
        {
            int category = (int)packet.Category;

            if (category >= 0 && category < m_packetOutboxes.Length)
            {
                LocklessQueue <OutgoingPacket> queue = m_packetOutboxes[category];
                TokenBucket bucket = m_throttleCategories[category];

                if (bucket.RemoveTokens(packet.Buffer.DataLength))
                {
                    // Enough tokens were removed from the bucket, the packet will not be queued
                    return(false);
                }
                else
                {
                    // Not enough tokens in the bucket, queue this packet
                    queue.Enqueue(packet);
                    return(true);
                }
            }
            else
            {
                // We don't have a token bucket for this category, so it will not be queued
                return(false);
            }
        }
Exemple #2
0
        public void ResendUnacked(LLAgent agent)
        {
            //FIXME: Make this an .ini setting
            const int AGENT_TIMEOUT_MS = 1000 * 60;

            // Disconnect an agent if no packets are received for some time
            if (Util.TickCount() - agent.TickLastPacketReceived > AGENT_TIMEOUT_MS)
            {
                m_log.Warn("Ack timeout, disconnecting " + agent.ID);

                ShutdownClient(agent);
                return;
            }

            // Get a list of all of the packets that have been sitting unacked longer than udpClient.RTO
            List <OutgoingPacket> expiredPackets = agent.NeedAcks.GetExpiredPackets(agent.RTO);

            if (expiredPackets != null)
            {
                //m_log.Debug("[LLUDPSERVER]: Resending " + expiredPackets.Count + " packets to " + udpClient.AgentID + ", RTO=" + udpClient.RTO);

                // Exponential backoff of the retransmission timeout
                agent.BackoffRTO();

                // Resend packets
                for (int i = 0; i < expiredPackets.Count; i++)
                {
                    OutgoingPacket outgoingPacket = expiredPackets[i];

                    //m_log.DebugFormat("[LLUDPSERVER]: Resending packet #{0} (attempt {1}), {2}ms have passed",
                    //    outgoingPacket.SequenceNumber, outgoingPacket.ResendCount, Environment.TickCount - outgoingPacket.TickCount);

                    // Set the resent flag
                    outgoingPacket.Buffer.Data[0] = (byte)(outgoingPacket.Buffer.Data[0] | Helpers.MSG_RESENT);
                    outgoingPacket.Category       = ThrottleCategory.Resend;

                    // Bump up the resend count on this packet
                    Interlocked.Increment(ref outgoingPacket.ResendCount);
                    Interlocked.Increment(ref m_packetsResent);

                    // Requeue or resend the packet
                    if (!agent.EnqueueOutgoing(outgoingPacket))
                    {
                        SendPacketFinal(outgoingPacket);
                    }
                }
            }
        }
 /// <summary>
 /// Add an unacked packet to the collection
 /// </summary>
 /// <param name="packet">Packet that is awaiting acknowledgement</param>
 /// <returns>True if the packet was successfully added, false if the
 /// packet already existed in the collection</returns>
 /// <remarks>This does not immediately add the ACK to the collection,
 /// it only queues it so it can be added in a thread-safe way later</remarks>
 public void Add(OutgoingPacket packet)
 {
     m_pendingAdds.Enqueue(packet);
 }
Exemple #4
0
        internal void SendPacketFinal(OutgoingPacket outgoingPacket)
        {
            const int MAX_APPENDED_ACKS = 250;

            UDPPacketBuffer buffer = outgoingPacket.Buffer;
            byte flags = buffer.Data[0];
            bool isResend = (flags & Helpers.MSG_RESENT) != 0;
            bool isReliable = (flags & Helpers.MSG_RELIABLE) != 0;
            bool isZerocoded = (flags & Helpers.MSG_ZEROCODED) != 0;
            LLAgent agent = outgoingPacket.Agent;

            if (!agent.IsConnected || buffer.RemoteEndPoint == null)
            {
                m_log.Debug("Dropping " + buffer.DataLength + " byte packet to client we have no route to");
                return;
            }

            int dataLength = buffer.DataLength;

            #region ACK Appending

            // NOTE: I'm seeing problems with some viewers when ACKs are appended to zerocoded packets so I've disabled that here
            if (!isZerocoded && outgoingPacket.Type != PacketType.PacketAck)
            {
                // Keep appending ACKs until there is no room left in the buffer or there are
                // no more ACKs to append
                byte ackCount = 0;
                uint ack;
                while (dataLength + 5 < buffer.Data.Length && ackCount < MAX_APPENDED_ACKS && agent.PendingAcks.TryDequeue(out ack))
                {
                    Utils.UIntToBytesBig(ack, buffer.Data, dataLength);
                    dataLength += 4;
                    ++ackCount;
                }

                if (ackCount > 0)
                {
                    // Set the last byte of the packet equal to the number of appended ACKs
                    buffer.Data[dataLength++] = ackCount;
                    // Set the appended ACKs flag on this packet
                    buffer.Data[0] |= Helpers.MSG_APPENDED_ACKS;
                }
            }

            #endregion ACK Appending

            buffer.DataLength = dataLength;

            #region Sequence Number Assignment

            if (!isResend)
            {
                // Not a resend, assign a new sequence number
                uint sequenceNumber = (uint)Interlocked.Increment(ref agent.CurrentSequence);
                Utils.UIntToBytesBig(sequenceNumber, buffer.Data, 1);
                outgoingPacket.SequenceNumber = sequenceNumber;

                if (isReliable)
                {
                    // Add this packet to the list of ACK responses we are waiting on from the server
                    agent.NeedAcks.Add(outgoingPacket);
                }
            }

            #endregion Sequence Number Assignment

            // Stats tracking
            Interlocked.Increment(ref agent.PacketsSent);
            Interlocked.Increment(ref m_packetsSent);
            if (isReliable)
                Interlocked.Add(ref agent.UnackedBytes, outgoingPacket.Buffer.DataLength);

            if (LoggingEnabled)
                m_log.Debug("<-- (" + buffer.RemoteEndPoint + ") " + outgoingPacket.Type);

            // Put the UDP payload on the wire
            Send(buffer);

            // Keep track of when this packet was sent out (right now)
            outgoingPacket.TickCount = Util.TickCount();

            //m_log.Debug("Sent " + outgoingPacket.Buffer.DataLength + " byte " + outgoingPacket.Category + " packet");
        }
Exemple #5
0
        public void SendPacketData(LLAgent agent, byte[] data, PacketType type, ThrottleCategory category)
        {
            int dataLength = data.Length;
            bool doZerocode = (data[0] & Helpers.MSG_ZEROCODED) != 0;
            bool doCopy = true;

            // Frequency analysis of outgoing packet sizes shows a large clump of packets at each end of the spectrum.
            // The vast majority of packets are less than 200 bytes, although due to asset transfers and packet splitting
            // there are a decent number of packets in the 1000-1140 byte range. We allocate one of two sizes of data here
            // to accomodate for both common scenarios and provide ample room for ACK appending in both
            int bufferSize = (dataLength > 180)
                ? LLUDPServer.MTU
                : 200;

            UDPPacketBuffer buffer = new UDPPacketBuffer(agent.RemoteEndPoint, bufferSize);

            // Zerocode if needed
            if (doZerocode)
            {
                try
                {
                    dataLength = Helpers.ZeroEncode(data, dataLength, buffer.Data);
                    doCopy = false;
                }
                catch (IndexOutOfRangeException)
                {
                    // The packet grew larger than the bufferSize while zerocoding.
                    // Remove the MSG_ZEROCODED flag and send the unencoded data
                    // instead
                    m_log.Debug("Packet exceeded buffer size during zerocoding for " + type + ". DataLength=" + dataLength +
                        " and BufferLength=" + buffer.Data.Length + ". Removing MSG_ZEROCODED flag");
                    data[0] = (byte)(data[0] & ~Helpers.MSG_ZEROCODED);
                }
            }

            // If the packet data wasn't already copied during zerocoding, copy it now
            if (doCopy)
            {
                if (dataLength > buffer.Data.Length)
                {
                    m_log.Error("Packet exceeded buffer size! This could be an indication of packet assembly not obeying the MTU. Type=" +
                        type + ", DataLength=" + dataLength + ", BufferLength=" + buffer.Data.Length);
                    buffer.Data = new byte[dataLength];
                }

                Buffer.BlockCopy(data, 0, buffer.Data, 0, dataLength);
            }

            buffer.DataLength = dataLength;

            #region Queue or Send

            OutgoingPacket outgoingPacket = new OutgoingPacket(agent, buffer, category, type);

            if (!agent.EnqueueOutgoing(outgoingPacket))
                SendPacketFinal(outgoingPacket);

            #endregion Queue or Send
        }
Exemple #6
0
 /// <summary>
 /// Add an unacked packet to the collection
 /// </summary>
 /// <param name="packet">Packet that is awaiting acknowledgement</param>
 /// <returns>True if the packet was successfully added, false if the
 /// packet already existed in the collection</returns>
 /// <remarks>This does not immediately add the ACK to the collection,
 /// it only queues it so it can be added in a thread-safe way later</remarks>
 public void Add(OutgoingPacket packet)
 {
     m_pendingAdds.Enqueue(packet);
 }
Exemple #7
0
        public bool EnqueueOutgoing(OutgoingPacket packet)
        {
            int category = (int)packet.Category;

            if (category >= 0 && category < m_packetOutboxes.Length)
            {
                LocklessQueue<OutgoingPacket> queue = m_packetOutboxes[category];
                TokenBucket bucket = m_throttleCategories[category];

                if (bucket.RemoveTokens(packet.Buffer.DataLength))
                {
                    // Enough tokens were removed from the bucket, the packet will not be queued
                    return false;
                }
                else
                {
                    // Not enough tokens in the bucket, queue this packet
                    queue.Enqueue(packet);
                    return true;
                }
            }
            else
            {
                // We don't have a token bucket for this category, so it will not be queued
                return false;
            }
        }
Exemple #8
0
        internal void SendPacketFinal(OutgoingPacket outgoingPacket)
        {
            const int MAX_APPENDED_ACKS = 250;

            UDPPacketBuffer buffer      = outgoingPacket.Buffer;
            byte            flags       = buffer.Data[0];
            bool            isResend    = (flags & Helpers.MSG_RESENT) != 0;
            bool            isReliable  = (flags & Helpers.MSG_RELIABLE) != 0;
            bool            isZerocoded = (flags & Helpers.MSG_ZEROCODED) != 0;
            LLAgent         agent       = outgoingPacket.Agent;

            if (!agent.IsConnected || buffer.RemoteEndPoint == null)
            {
                m_log.Debug("Dropping " + buffer.DataLength + " byte packet to client we have no route to");
                return;
            }

            int dataLength = buffer.DataLength;

            #region ACK Appending

            // NOTE: I'm seeing problems with some viewers when ACKs are appended to zerocoded packets so I've disabled that here
            if (!isZerocoded && outgoingPacket.Type != PacketType.PacketAck)
            {
                // Keep appending ACKs until there is no room left in the buffer or there are
                // no more ACKs to append
                byte ackCount = 0;
                uint ack;
                while (dataLength + 5 < buffer.Data.Length && ackCount < MAX_APPENDED_ACKS && agent.PendingAcks.TryDequeue(out ack))
                {
                    Utils.UIntToBytesBig(ack, buffer.Data, dataLength);
                    dataLength += 4;
                    ++ackCount;
                }

                if (ackCount > 0)
                {
                    // Set the last byte of the packet equal to the number of appended ACKs
                    buffer.Data[dataLength++] = ackCount;
                    // Set the appended ACKs flag on this packet
                    buffer.Data[0] |= Helpers.MSG_APPENDED_ACKS;
                }
            }

            #endregion ACK Appending

            buffer.DataLength = dataLength;

            #region Sequence Number Assignment

            if (!isResend)
            {
                // Not a resend, assign a new sequence number
                uint sequenceNumber = (uint)Interlocked.Increment(ref agent.CurrentSequence);
                Utils.UIntToBytesBig(sequenceNumber, buffer.Data, 1);
                outgoingPacket.SequenceNumber = sequenceNumber;

                if (isReliable)
                {
                    // Add this packet to the list of ACK responses we are waiting on from the server
                    agent.NeedAcks.Add(outgoingPacket);
                }
            }

            #endregion Sequence Number Assignment

            // Stats tracking
            Interlocked.Increment(ref agent.PacketsSent);
            Interlocked.Increment(ref m_packetsSent);
            if (isReliable)
            {
                Interlocked.Add(ref agent.UnackedBytes, outgoingPacket.Buffer.DataLength);
            }

            if (LoggingEnabled)
            {
                m_log.Debug("<-- (" + buffer.RemoteEndPoint + ") " + outgoingPacket.Type);
            }

            // Put the UDP payload on the wire
            Send(buffer);

            // Keep track of when this packet was sent out (right now)
            outgoingPacket.TickCount = Util.TickCount();

            //m_log.Debug("Sent " + outgoingPacket.Buffer.DataLength + " byte " + outgoingPacket.Category + " packet");
        }
Exemple #9
0
        public void SendPacketData(LLAgent agent, byte[] data, PacketType type, ThrottleCategory category)
        {
            int  dataLength = data.Length;
            bool doZerocode = (data[0] & Helpers.MSG_ZEROCODED) != 0;
            bool doCopy     = true;

            // Frequency analysis of outgoing packet sizes shows a large clump of packets at each end of the spectrum.
            // The vast majority of packets are less than 200 bytes, although due to asset transfers and packet splitting
            // there are a decent number of packets in the 1000-1140 byte range. We allocate one of two sizes of data here
            // to accomodate for both common scenarios and provide ample room for ACK appending in both
            int bufferSize = (dataLength > 180)
                ? LLUDPServer.MTU
                : 200;

            UDPPacketBuffer buffer = new UDPPacketBuffer(agent.RemoteEndPoint, bufferSize);

            // Zerocode if needed
            if (doZerocode)
            {
                try
                {
                    dataLength = Helpers.ZeroEncode(data, dataLength, buffer.Data);
                    doCopy     = false;
                }
                catch (IndexOutOfRangeException)
                {
                    // The packet grew larger than the bufferSize while zerocoding.
                    // Remove the MSG_ZEROCODED flag and send the unencoded data
                    // instead
                    m_log.Debug("Packet exceeded buffer size during zerocoding for " + type + ". DataLength=" + dataLength +
                                " and BufferLength=" + buffer.Data.Length + ". Removing MSG_ZEROCODED flag");
                    data[0] = (byte)(data[0] & ~Helpers.MSG_ZEROCODED);
                }
            }

            // If the packet data wasn't already copied during zerocoding, copy it now
            if (doCopy)
            {
                if (dataLength > buffer.Data.Length)
                {
                    m_log.Error("Packet exceeded buffer size! This could be an indication of packet assembly not obeying the MTU. Type=" +
                                type + ", DataLength=" + dataLength + ", BufferLength=" + buffer.Data.Length);
                    buffer.Data = new byte[dataLength];
                }

                Buffer.BlockCopy(data, 0, buffer.Data, 0, dataLength);
            }

            buffer.DataLength = dataLength;

            #region Queue or Send

            OutgoingPacket outgoingPacket = new OutgoingPacket(agent, buffer, category, type);

            if (!agent.EnqueueOutgoing(outgoingPacket))
            {
                SendPacketFinal(outgoingPacket);
            }

            #endregion Queue or Send
        }
Exemple #10
0
        /// <summary>
        /// Loops through all of the packet queues for this client and tries to send
        /// any outgoing packets, obeying the throttling bucket limits
        /// </summary>
        /// <remarks>This function is only called from a synchronous loop in the
        /// UDPServer so we don't need to bother making this thread safe</remarks>
        /// <returns>True if any packets were sent, otherwise false</returns>
        public bool DequeueOutgoing()
        {
            OutgoingPacket packet;
            LocklessQueue <OutgoingPacket> queue;
            TokenBucket           bucket;
            bool                  packetSent      = false;
            ThrottleCategoryFlags emptyCategories = 0;

            //string queueDebugOutput = String.Empty; // Serious debug business

            for (int i = 0; i < THROTTLE_CATEGORY_COUNT; i++)
            {
                bucket = m_throttleCategories[i];
                //queueDebugOutput += m_packetOutboxes[i].Count + " ";  // Serious debug business

                if (m_nextPackets[i] != null)
                {
                    // This bucket was empty the last time we tried to send a packet,
                    // leaving a dequeued packet still waiting to be sent out. Try to
                    // send it again
                    OutgoingPacket nextPacket = m_nextPackets[i];
                    if (bucket.RemoveTokens(nextPacket.Buffer.DataLength))
                    {
                        // Send the packet
                        m_udpServer.SendPacketFinal(nextPacket);
                        m_nextPackets[i] = null;
                        packetSent       = true;
                    }
                }
                else
                {
                    // No dequeued packet waiting to be sent, try to pull one off
                    // this queue
                    queue = m_packetOutboxes[i];
                    if (queue.TryDequeue(out packet))
                    {
                        // A packet was pulled off the queue. See if we have
                        // enough tokens in the bucket to send it out
                        if (bucket.RemoveTokens(packet.Buffer.DataLength))
                        {
                            // Send the packet
                            m_udpServer.SendPacketFinal(packet);
                            packetSent = true;
                        }
                        else
                        {
                            // Save the dequeued packet for the next iteration
                            m_nextPackets[i] = packet;
                        }

                        // If the queue is empty after this dequeue, fire the queue
                        // empty callback now so it has a chance to fill before we
                        // get back here
                        if (queue.Count == 0)
                        {
                            emptyCategories |= CategoryToFlag(i);
                        }
                    }
                    else
                    {
                        // No packets in this queue. Fire the queue empty callback
                        // if it has not been called recently
                        emptyCategories |= CategoryToFlag(i);
                    }
                }
            }

            if (emptyCategories != 0)
            {
                BeginFireQueueEmpty(emptyCategories);
            }

            //m_log.Info("[LLUDPCLIENT]: Queues: " + queueDebugOutput); // Serious debug business
            return(packetSent);
        }