private SendPacketFinal ( OutgoingPacket outgoingPacket ) : void | ||
outgoingPacket | OutgoingPacket | |
return | void |
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.Dequeue(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
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);
}
}
#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);
// Keep track of when this packet was sent out (right now)
outgoingPacket.TickCount = Environment.TickCount & Int32.MaxValue;
}
/// <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() { OutgoingPacket packet; OpenSim.Framework.LocklessQueue <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; } } else { // No dequeued packet waiting to be sent, try to pull one off // this queue queue = m_packetOutboxes[i]; 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 (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); }