/// <summary> /// Default constructor /// </summary> /// <param name="server">Reference to the UDP server this client is connected to</param> /// <param name="rates">Default throttling rates and maximum throttle limits</param> /// <param name="parentThrottle">Parent HTB (hierarchical token bucket) /// that the child throttles will be governed by</param> /// <param name="circuitCode">Circuit code for this connection</param> /// <param name="agentID">AgentID for the connected agent</param> /// <param name="remoteEndPoint">Remote endpoint for this connection</param> /// <param name="defaultRTO"> /// Default retransmission timeout for unacked packets. The RTO will never drop /// beyond this number. /// </param> /// <param name="maxRTO"> /// The maximum retransmission timeout for unacked packets. The RTO will never exceed this number. /// </param> public LLUDPClient( LLUDPServer server, ThrottleRates rates, TokenBucket parentThrottle, uint circuitCode, UUID agentID, IPEndPoint remoteEndPoint, int defaultRTO, int maxRTO) { AgentID = agentID; RemoteEndPoint = remoteEndPoint; CircuitCode = circuitCode; m_udpServer = server; if (defaultRTO != 0) { m_defaultRTO = defaultRTO; } if (maxRTO != 0) { m_maxRTO = maxRTO; } m_burstTime = rates.BrustTime; float m_burst = rates.ClientMaxRate * m_burstTime; // Create a token bucket throttle for this client that has the scene token bucket as a parent m_throttleClient = new AdaptiveTokenBucket(parentThrottle, rates.ClientMaxRate, m_burst, rates.AdaptiveThrottlesEnabled); // Create an array of token buckets for this clients different throttle categories m_throttleCategories = new TokenBucket[THROTTLE_CATEGORY_COUNT]; m_burst = rates.Total * rates.BrustTime; for (int i = 0; i < THROTTLE_CATEGORY_COUNT; i++) { ThrottleOutPacketType type = (ThrottleOutPacketType)i; // Initialize the packet outboxes, where packets sit while they are waiting for tokens m_packetOutboxes[i] = new DoubleLocklessQueue <OutgoingPacket>(); // Initialize the token buckets that control the throttling for each category //m_throttleCategories[i] = new TokenBucket(m_throttleClient, rates.GetRate(type), m_burst); float rate = rates.GetRate(type); float burst = rate * rates.BrustTime; m_throttleCategories[i] = new TokenBucket(m_throttleClient, rate, burst); } // Default the retransmission timeout to one second m_RTO = m_defaultRTO; // Initialize this to a sane value to prevent early disconnects TickLastPacketReceived = Environment.TickCount & Int32.MaxValue; m_pingMS = (int)(3.0 * server.TickCountResolution); // so filter doesnt start at 0; }
/// <summary> /// Queue an outgoing packet if appropriate. /// </summary> /// <param name="packet"></param> /// <param name="forceQueue">Always queue the packet if at all possible.</param> /// <returns> /// true if the packet has been queued, /// false if the packet has not been queued and should be sent immediately. /// </returns> public bool EnqueueOutgoing(OutgoingPacket packet) { int category = (int)packet.Category; if (category >= 0 && category < m_packetOutboxes.Length) { DoubleLocklessQueue <OutgoingPacket> queue = m_packetOutboxes[category]; queue.Enqueue(packet, false); return(true); } else { // We don't have a token bucket for this category, so it will not be queued return(false); } }
public bool EnqueueOutgoing(OutgoingPacket packet, bool forceQueue, bool highPriority) { int category = (int)packet.Category; if (category >= 0 && category < m_packetOutboxes.Length) { DoubleLocklessQueue <OutgoingPacket> queue = m_packetOutboxes[category]; if (m_deliverPackets == false) { queue.Enqueue(packet, highPriority); return(true); } TokenBucket bucket = m_throttleCategories[category]; // Don't send this packet if queue is not empty if (queue.Count > 0 || m_nextPackets[category] != null) { queue.Enqueue(packet, highPriority); return(true); } if (!forceQueue && bucket.CheckTokens(packet.Buffer.DataLength)) { // enough tokens so it can be sent imediatly by caller bucket.RemoveTokens(packet.Buffer.DataLength); return(false); } else { // Force queue specified or not enough tokens in the bucket, queue this packet queue.Enqueue(packet, highPriority); return(true); } } else { // We don't have a token bucket for this category, so it will not be queued return(false); } }
/// <summary> /// Loops through all of the packet queues for this client and tries to send /// an outgoing packet from each, obeying the throttling bucket limits /// </summary> /// /// <remarks> /// Packet queues are inspected in ascending numerical order starting from 0. Therefore, queues with a lower /// ThrottleOutPacketType number will see their packet get sent first (e.g. if both Land and Wind queues have /// packets, then the packet at the front of the Land queue will be sent before the packet at the front of the /// wind queue). /// /// 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() { // if (m_deliverPackets == false) return false; OutgoingPacket packet = null; DoubleLocklessQueue <OutgoingPacket> queue; bool packetSent = false; ThrottleOutPacketTypeFlags emptyCategories = 0; //string queueDebugOutput = String.Empty; // Serious debug business // do resends packet = m_nextPackets[0]; if (packet != null) { if (packet.Buffer != null) { if (m_throttleCategories[0].RemoveTokens(packet.Buffer.DataLength)) { // Send the packet m_udpServer.SendPacketFinal(packet); packetSent = true; m_nextPackets[0] = null; } } else { m_nextPackets[0] = null; } } else { queue = m_packetOutboxes[0]; if (queue != null) { if (queue.Dequeue(out packet)) { // A packet was pulled off the queue. See if we have // enough tokens in the bucket to send it out if (packet.Buffer != null) { if (m_throttleCategories[0].RemoveTokens(packet.Buffer.DataLength)) { // Send the packet m_udpServer.SendPacketFinal(packet); packetSent = true; } else { // Save the dequeued packet for the next iteration m_nextPackets[0] = packet; } } } } else { m_packetOutboxes[0] = new DoubleLocklessQueue <OutgoingPacket>(); } } if (NeedAcks.Count() > 50) { Interlocked.Increment(ref AckStalls); return(true); } for (int i = 1; i < THROTTLE_CATEGORY_COUNT; i++) { //queueDebugOutput += m_packetOutboxes[i].Count + " "; // Serious debug business packet = m_nextPackets[i]; if (packet != null) { if (packet.Buffer == null) { if (m_packetOutboxes[i].Count < 5) { emptyCategories |= CategoryToFlag(i); } m_nextPackets[i] = null; continue; } if (m_throttleCategories[i].RemoveTokens(packet.Buffer.DataLength)) { // Send the packet m_udpServer.SendPacketFinal(packet); m_nextPackets[i] = null; packetSent = true; if (m_packetOutboxes[i].Count < 5) { emptyCategories |= CategoryToFlag(i); } } } else { // No dequeued packet waiting to be sent, try to pull one off // this queue queue = m_packetOutboxes[i]; if (queue.Dequeue(out packet)) { if (packet.Buffer == null) { // packet canceled elsewhere (by a ack for example) if (queue.Count < 5) { emptyCategories |= CategoryToFlag(i); } continue; } if (m_throttleCategories[i].RemoveTokens(packet.Buffer.DataLength)) { // Send the packet m_udpServer.SendPacketFinal(packet); packetSent = true; if (queue.Count < 5) { emptyCategories |= CategoryToFlag(i); } } else { // Save the dequeued packet for the next iteration m_nextPackets[i] = packet; } } 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); }
/// <summary> /// Loops through all of the packet queues for this client and tries to send /// an outgoing packet from each, obeying the throttling bucket limits /// </summary> /// /// <remarks> /// Packet queues are inspected in ascending numerical order starting from 0. Therefore, queues with a lower /// ThrottleOutPacketType number will see their packet get sent first (e.g. if both Land and Wind queues have /// packets, then the packet at the front of the Land queue will be sent before the packet at the front of the /// wind queue). /// /// 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() { // if (m_deliverPackets == false) return false; OutgoingPacket packet = null; DoubleLocklessQueue <OutgoingPacket> queue; TokenBucket bucket; bool packetSent = false; ThrottleOutPacketTypeFlags 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; if (m_packetOutboxes[i].Count < 5) { emptyCategories |= CategoryToFlag(i); } } } else { // No dequeued packet waiting to be sent, try to pull one off // this queue queue = m_packetOutboxes[i]; if (queue != null) { bool success = false; try { success = queue.Dequeue(out packet); } catch { m_packetOutboxes[i] = new DoubleLocklessQueue <OutgoingPacket>(); } if (success) { // 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; if (queue.Count < 5) { emptyCategories |= CategoryToFlag(i); } } else { // Save the dequeued packet for the next iteration m_nextPackets[i] = packet; } } else { // No packets in this queue. Fire the queue empty callback // if it has not been called recently emptyCategories |= CategoryToFlag(i); } } else { m_packetOutboxes[i] = new DoubleLocklessQueue <OutgoingPacket>(); emptyCategories |= CategoryToFlag(i); } } } if (emptyCategories != 0) { BeginFireQueueEmpty(emptyCategories); } //m_log.Info("[LLUDPCLIENT]: Queues: " + queueDebugOutput); // Serious debug business return(packetSent); }