public bool EnqueueOutgoing(OutgoingPacket packet) { int category = (int)packet.Category; if (category >= 0 && category < (int)ThrottleOutPacketType.Count) { //All packets are enqueued, except those that don't have a queue int prio = MapCatsToPriority[category]; m_outbox.Enqueue(prio, packet); return(true); } // We don't have a token bucket for this category, // so it will not be queued and sent immediately return(false); }
public bool Dequeue(out OutgoingPacket pack) { int i = nlevels; while (--i >= 0) // go down levels looking for data { object o; if (!queues[i].TryDequeue(out o)) { continue; } if (!(o is OutgoingPacket)) { continue; } pack = (OutgoingPacket)o; Interlocked.Decrement(ref Count); return(true); // else do call to a funtion that will return the packet or whatever } pack = null; return(false); }
/// <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); }
/// <summary> /// tries to send queued packets /// </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(int MaxNPacks) { bool packetSent = false; for (int i = 0; i < MaxNPacks; i++) { OutgoingPacket packet; if (m_nextOutPacket != null) { packet = m_nextOutPacket; if (m_throttle.RemoveTokens(packet.Buffer.DataLength)) { // Send the packet m_udpServer.SendPacketFinal(packet); m_nextOutPacket = null; packetSent = true; } } // No dequeued packet waiting to be sent, try to pull one off // this queue else if (m_outbox.Dequeue(out packet)) { MainConsole.Instance.Format(Level.All, AgentID + " - " + packet.Packet.Type); // A packet was pulled off the queue. See if we have // enough tokens in the bucket to send it out if (packet.Category == ThrottleOutPacketType.OutBand || m_throttle.RemoveTokens(packet.Buffer.DataLength)) { packetSent = true; //Send the packet PacketsCounts[(int)packet.Category] += packet.Packet.Length; m_udpServer.SendPacketFinal(packet); PacketsSent++; } else { m_nextOutPacket = packet; break; } } else { break; } } if (packetSent) { if (m_throttle.MaxBurst < TotalRateRequested) { float tmp = m_throttle.MaxBurst * 1.005f; m_throttle.DripRate = (int)tmp; m_throttle.MaxBurst = (int)tmp; } } if (m_nextOnQueueEmpty != 0 && Util.EnvironmentTickCountSubtract(m_nextOnQueueEmpty) >= 0) { // Use a value of 0 to signal that FireQueueEmpty is running m_nextOnQueueEmpty = 0; // Asynchronously run the callback int ptmp = m_outbox.queues[MapCatsToPriority[(int)ThrottleOutPacketType.Task]].Count; int atmp = m_outbox.queues[MapCatsToPriority[(int)ThrottleOutPacketType.AvatarInfo]].Count; int ttmp = m_outbox.queues[MapCatsToPriority[(int)ThrottleOutPacketType.Texture]].Count; int[] arg = { ptmp, atmp, ttmp }; Util.FireAndForget(FireQueueEmpty, arg); } return(packetSent); }
/// <summary> /// Actually send a packet to a client. /// </summary> /// <param name="outgoingPacket"></param> internal void SendPacketFinal(OutgoingPacket outgoingPacket) { 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; LLUDPClient udpClient = outgoingPacket.Client; if (!udpClient.IsConnected) return; #region ACK Appending int dataLength = buffer.DataLength; // NOTE: I'm seeing problems with some viewers when ACKs are appended to zerocoded packets so I've disabled that here if (!isZerocoded) { // Keep appending ACKs until there is no room left in the buffer or there are // no more ACKs to append uint ackCount = 0; uint ack; while (dataLength + 5 < buffer.Data.Length && udpClient.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++] = (byte) ackCount; // Set the appended ACKs flag on this packet buffer.Data[0] = (byte) (buffer.Data[0] | Helpers.MSG_APPENDED_ACKS); } } buffer.DataLength = dataLength; #endregion ACK Appending #region Sequence Number Assignment bool canBeRemoved = false; if (!isResend) { // Not a resend, assign a new sequence number uint sequenceNumber = (uint) Interlocked.Increment(ref udpClient.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 udpClient.NeedAcks.Add(outgoingPacket); Interlocked.Add(ref udpClient.UnackedBytes, outgoingPacket.Buffer.DataLength); } else canBeRemoved = true; } #endregion Sequence Number Assignment // Stats tracking Interlocked.Increment(ref udpClient.PacketsSent); // if (isReliable) // Interlocked.Add(ref udpClient.UnackedBytes, outgoingPacket.Buffer.DataLength); // Put the UDP payload on the wire // AsyncBeginSend(buffer); SyncSend(buffer); // Keep track of when this packet was sent out (right now) outgoingPacket.TickCount = Environment.TickCount & Int32.MaxValue; if (outgoingPacket.FinishedMethod != null) outgoingPacket.FinishedMethod(outgoingPacket); if (canBeRemoved) outgoingPacket.Destroy(1); }
public void SendPacketData(LLUDPClient udpClient, byte[] data, Packet packet, ThrottleOutPacketType category, UnackedPacketMethod resendMethod, UnackedPacketMethod finishedMethod) { 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*2; UDPPacketBuffer buffer = new UDPPacketBuffer(udpClient.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 MainConsole.Instance.Debug("[LLUDPSERVER]: Packet exceeded buffer size during zerocoding for " + packet.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) { Buffer.BlockCopy(data, 0, buffer.Data, 0, dataLength); } else { bufferSize = dataLength; buffer = new UDPPacketBuffer(udpClient.RemoteEndPoint, bufferSize); // MainConsole.Instance.Error("[LLUDPSERVER]: Packet exceeded buffer size! This could be an indication of packet assembly not obeying the MTU. Type=" + // type + ", DataLength=" + dataLength + ", BufferLength=" + buffer.Data.Length + ". Dropping packet"); Buffer.BlockCopy(data, 0, buffer.Data, 0, dataLength); } } buffer.DataLength = dataLength; #region Queue or Send OutgoingPacket outgoingPacket = new OutgoingPacket(udpClient, buffer, category, resendMethod, finishedMethod, packet); if (!outgoingPacket.Client.EnqueueOutgoing(outgoingPacket)) SendPacketFinal(outgoingPacket); #endregion Queue or Send }
public bool Dequeue(out OutgoingPacket pack) { int i = nlevels; while (--i >= 0) // go down levels looking for data { object o; if (!queues[i].TryDequeue(out o)) continue; if (!(o is OutgoingPacket)) continue; pack = (OutgoingPacket) o; Interlocked.Decrement(ref Count); return true; // else do call to a funtion that will return the packet or whatever } pack = null; return false; }
public bool EnqueueOutgoing(OutgoingPacket packet) { int category = (int) packet.Category; if (category >= 0 && category < (int) ThrottleOutPacketType.Count) { //All packets are enqueued, except those that don't have a queue int prio = MapCatsToPriority[category]; m_outbox.Enqueue(prio, packet); return true; } // We don't have a token bucket for this category, // so it will not be queued and sent immediately return false; }
/// <summary> /// tries to send queued packets /// </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(int MaxNPacks) { bool packetSent = false; for (int i = 0; i < MaxNPacks; i++) { OutgoingPacket packet; if (m_nextOutPacket != null) { packet = m_nextOutPacket; if (m_throttle.RemoveTokens(packet.Buffer.DataLength)) { // Send the packet m_udpServer.SendPacketFinal(packet); m_nextOutPacket = null; packetSent = true; } } // No dequeued packet waiting to be sent, try to pull one off // this queue else if (m_outbox.Dequeue(out packet)) { MainConsole.Instance.Format(Level.All, AgentID + " - " + packet.Packet.Type); // A packet was pulled off the queue. See if we have // enough tokens in the bucket to send it out if (packet.Category == ThrottleOutPacketType.OutBand || m_throttle.RemoveTokens(packet.Buffer.DataLength)) { packetSent = true; //Send the packet PacketsCounts[(int) packet.Category] += packet.Packet.Length; m_udpServer.SendPacketFinal(packet); PacketsSent++; } else { m_nextOutPacket = packet; break; } } else break; } if (packetSent) { if (m_throttle.MaxBurst < TotalRateRequested) { float tmp = m_throttle.MaxBurst*1.005f; m_throttle.DripRate = (int) tmp; m_throttle.MaxBurst = (int) tmp; } } if (m_nextOnQueueEmpty != 0 && Util.EnvironmentTickCountSubtract(m_nextOnQueueEmpty) >= 0) { // Use a value of 0 to signal that FireQueueEmpty is running m_nextOnQueueEmpty = 0; // Asynchronously run the callback int ptmp = m_outbox.queues[MapCatsToPriority[(int) ThrottleOutPacketType.Task]].Count; int atmp = m_outbox.queues[MapCatsToPriority[(int) ThrottleOutPacketType.AvatarInfo]].Count; int ttmp = m_outbox.queues[MapCatsToPriority[(int) ThrottleOutPacketType.Texture]].Count; int[] arg = {ptmp, atmp, ttmp}; Util.FireAndForget(FireQueueEmpty, arg); } return packetSent; }