123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697 |
- using System;
- using System.Collections.Generic;
- using System.Net;
- using System.Threading;
- using log4net;
- using OpenSim.Framework;
- using OpenMetaverse;
- using OpenMetaverse.Packets;
- using TokenBucket = OpenSim.Region.ClientStack.LindenUDP.TokenBucket;
- namespace OpenSim.Region.ClientStack.LindenUDP
- {
- #region Delegates
-
-
-
-
-
-
-
-
-
- public delegate void PacketStats(int inPackets, int outPackets, int unAckedBytes);
-
-
-
-
-
- public delegate void QueueEmpty(ThrottleOutPacketTypeFlags categories);
- #endregion Delegates
-
-
-
- public sealed class LLUDPClient
- {
-
-
-
- const float STATE_TASK_PERCENTAGE = 0.8f;
- private static readonly ILog m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
-
-
- const int THROTTLE_CATEGORY_COUNT = 8;
-
- public event PacketStats OnPacketStats;
-
-
- public event QueueEmpty OnQueueEmpty;
-
- public readonly UUID AgentID;
-
- public readonly IPEndPoint RemoteEndPoint;
-
- public readonly uint CircuitCode;
-
- public readonly IncomingPacketHistoryCollection PacketArchive = new IncomingPacketHistoryCollection(200);
-
- public readonly UnackedPacketCollection NeedAcks = new UnackedPacketCollection();
-
- public readonly OpenSim.Framework.LocklessQueue<uint> PendingAcks = new OpenSim.Framework.LocklessQueue<uint>();
-
- public int CurrentSequence;
-
- public byte CurrentPingSequence;
-
- public bool IsConnected = true;
-
- public bool IsPaused;
-
- public int TickLastPacketReceived;
-
-
- public float SRTT;
-
- public float RTTVAR;
-
-
-
-
- public int RTO;
-
-
- public int BytesSinceLastACK;
-
- public int PacketsReceived;
-
- public int PacketsSent;
-
- public int PacketsResent;
-
- public int UnackedBytes;
-
- private int m_packetsReceivedReported;
-
- private int m_packetsSentReported;
-
- private int m_nextOnQueueEmpty = 1;
-
- private readonly AdaptiveTokenBucket m_throttleClient;
- public AdaptiveTokenBucket FlowThrottle
- {
- get { return m_throttleClient; }
- }
-
- private readonly TokenBucket m_throttleCategory;
-
- private readonly TokenBucket[] m_throttleCategories;
-
- private readonly OpenSim.Framework.LocklessQueue<OutgoingPacket>[] m_packetOutboxes = new OpenSim.Framework.LocklessQueue<OutgoingPacket>[THROTTLE_CATEGORY_COUNT];
-
-
- private readonly OutgoingPacket[] m_nextPackets = new OutgoingPacket[THROTTLE_CATEGORY_COUNT];
-
- private readonly LLUDPServer m_udpServer;
-
- private byte[] m_packedThrottles;
- private int m_defaultRTO = 1000;
- private int m_maxRTO = 60000;
-
-
-
-
-
-
-
-
-
-
- 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_throttleClient = new AdaptiveTokenBucket(parentThrottle, rates.Total, rates.AdaptiveThrottlesEnabled);
-
- m_throttleCategory = new TokenBucket(m_throttleClient, 0);
-
- m_throttleCategories = new TokenBucket[THROTTLE_CATEGORY_COUNT];
- for (int i = 0; i < THROTTLE_CATEGORY_COUNT; i++)
- {
- ThrottleOutPacketType type = (ThrottleOutPacketType)i;
-
- m_packetOutboxes[i] = new OpenSim.Framework.LocklessQueue<OutgoingPacket>();
-
- m_throttleCategories[i] = new TokenBucket(m_throttleCategory, rates.GetRate(type));
- }
-
- RTO = m_defaultRTO;
-
- TickLastPacketReceived = Environment.TickCount & Int32.MaxValue;
- }
-
-
-
- public void Shutdown()
- {
- IsConnected = false;
- for (int i = 0; i < THROTTLE_CATEGORY_COUNT; i++)
- {
- m_packetOutboxes[i].Clear();
- m_nextPackets[i] = null;
- }
-
- m_throttleClient.Parent.UnregisterRequest(m_throttleClient);
- OnPacketStats = null;
- OnQueueEmpty = null;
- }
-
-
-
-
- public ClientInfo GetClientInfo()
- {
-
-
-
- ClientInfo info = new ClientInfo();
- info.pendingAcks = new Dictionary<uint, uint>();
- info.needAck = new Dictionary<uint, byte[]>();
- info.resendThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Resend].DripRate;
- info.landThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Land].DripRate;
- info.windThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Wind].DripRate;
- info.cloudThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Cloud].DripRate;
- info.taskThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Task].DripRate;
- info.assetThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Asset].DripRate;
- info.textureThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Texture].DripRate;
- info.totalThrottle = (int)m_throttleCategory.DripRate;
- return info;
- }
-
-
-
-
- public void SetClientInfo(ClientInfo info)
- {
-
-
-
- throw new NotImplementedException();
- }
-
-
-
-
-
-
-
- public string GetStats()
- {
- return string.Format(
- "{0,7} {1,7} {2,7} {3,9} {4,7} {5,7} {6,7} {7,7} {8,7} {9,8} {10,7} {11,7}",
- PacketsReceived,
- PacketsSent,
- PacketsResent,
- UnackedBytes,
- m_packetOutboxes[(int)ThrottleOutPacketType.Resend].Count,
- m_packetOutboxes[(int)ThrottleOutPacketType.Land].Count,
- m_packetOutboxes[(int)ThrottleOutPacketType.Wind].Count,
- m_packetOutboxes[(int)ThrottleOutPacketType.Cloud].Count,
- m_packetOutboxes[(int)ThrottleOutPacketType.Task].Count,
- m_packetOutboxes[(int)ThrottleOutPacketType.Texture].Count,
- m_packetOutboxes[(int)ThrottleOutPacketType.Asset].Count,
- m_packetOutboxes[(int)ThrottleOutPacketType.State].Count);
- }
- public void SendPacketStats()
- {
- PacketStats callback = OnPacketStats;
- if (callback != null)
- {
- int newPacketsReceived = PacketsReceived - m_packetsReceivedReported;
- int newPacketsSent = PacketsSent - m_packetsSentReported;
- callback(newPacketsReceived, newPacketsSent, UnackedBytes);
- m_packetsReceivedReported += newPacketsReceived;
- m_packetsSentReported += newPacketsSent;
- }
- }
- public void SetThrottles(byte[] throttleData)
- {
- byte[] adjData;
- int pos = 0;
- if (!BitConverter.IsLittleEndian)
- {
- byte[] newData = new byte[7 * 4];
- Buffer.BlockCopy(throttleData, 0, newData, 0, 7 * 4);
- for (int i = 0; i < 7; i++)
- Array.Reverse(newData, i * 4, 4);
- adjData = newData;
- }
- else
- {
- adjData = throttleData;
- }
-
- int resend = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4;
- int land = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4;
- int wind = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4;
- int cloud = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4;
- int task = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4;
- int texture = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4;
- int asset = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f);
-
- int state = 0;
-
-
- resend = Math.Max(resend, LLUDPServer.MTU);
- land = Math.Max(land, LLUDPServer.MTU);
- wind = Math.Max(wind, LLUDPServer.MTU);
- cloud = Math.Max(cloud, LLUDPServer.MTU);
- task = Math.Max(task, LLUDPServer.MTU);
- texture = Math.Max(texture, LLUDPServer.MTU);
- asset = Math.Max(asset, LLUDPServer.MTU);
-
-
-
-
- TokenBucket bucket;
- bucket = m_throttleCategories[(int)ThrottleOutPacketType.Resend];
- bucket.RequestedDripRate = resend;
- bucket = m_throttleCategories[(int)ThrottleOutPacketType.Land];
- bucket.RequestedDripRate = land;
- bucket = m_throttleCategories[(int)ThrottleOutPacketType.Wind];
- bucket.RequestedDripRate = wind;
- bucket = m_throttleCategories[(int)ThrottleOutPacketType.Cloud];
- bucket.RequestedDripRate = cloud;
- bucket = m_throttleCategories[(int)ThrottleOutPacketType.Asset];
- bucket.RequestedDripRate = asset;
- bucket = m_throttleCategories[(int)ThrottleOutPacketType.Task];
- bucket.RequestedDripRate = task;
- bucket = m_throttleCategories[(int)ThrottleOutPacketType.State];
- bucket.RequestedDripRate = state;
- bucket = m_throttleCategories[(int)ThrottleOutPacketType.Texture];
- bucket.RequestedDripRate = texture;
-
- m_packedThrottles = null;
- }
- public byte[] GetThrottlesPacked(float multiplier)
- {
- byte[] data = m_packedThrottles;
- if (data == null)
- {
- float rate;
- data = new byte[7 * 4];
- int i = 0;
-
- rate = (float)m_throttleCategories[(int)ThrottleOutPacketType.Resend].RequestedDripRate * 8 * multiplier;
- Buffer.BlockCopy(Utils.FloatToBytes(rate), 0, data, i, 4); i += 4;
- rate = (float)m_throttleCategories[(int)ThrottleOutPacketType.Land].RequestedDripRate * 8 * multiplier;
- Buffer.BlockCopy(Utils.FloatToBytes(rate), 0, data, i, 4); i += 4;
- rate = (float)m_throttleCategories[(int)ThrottleOutPacketType.Wind].RequestedDripRate * 8 * multiplier;
- Buffer.BlockCopy(Utils.FloatToBytes(rate), 0, data, i, 4); i += 4;
- rate = (float)m_throttleCategories[(int)ThrottleOutPacketType.Cloud].RequestedDripRate * 8 * multiplier;
- Buffer.BlockCopy(Utils.FloatToBytes(rate), 0, data, i, 4); i += 4;
- rate = (float)m_throttleCategories[(int)ThrottleOutPacketType.Task].RequestedDripRate * 8 * multiplier;
- Buffer.BlockCopy(Utils.FloatToBytes(rate), 0, data, i, 4); i += 4;
- rate = (float)m_throttleCategories[(int)ThrottleOutPacketType.Texture].RequestedDripRate * 8 * multiplier;
- Buffer.BlockCopy(Utils.FloatToBytes(rate), 0, data, i, 4); i += 4;
- rate = (float)m_throttleCategories[(int)ThrottleOutPacketType.Asset].RequestedDripRate * 8 * multiplier;
- Buffer.BlockCopy(Utils.FloatToBytes(rate), 0, data, i, 4); i += 4;
- m_packedThrottles = data;
- }
- return data;
- }
-
-
-
-
-
-
-
-
-
- public bool EnqueueOutgoing(OutgoingPacket packet, bool forceQueue)
- {
- int category = (int)packet.Category;
- if (category >= 0 && category < m_packetOutboxes.Length)
- {
- OpenSim.Framework.LocklessQueue<OutgoingPacket> queue = m_packetOutboxes[category];
- TokenBucket bucket = m_throttleCategories[category];
-
-
-
- if (queue.Count > 0)
- {
- queue.Enqueue(packet);
- return true;
- }
-
-
- if (!forceQueue && bucket.RemoveTokens(packet.Buffer.DataLength))
- {
-
- return false;
- }
- else
- {
-
- queue.Enqueue(packet);
- return true;
- }
- }
- else
- {
-
- return false;
- }
- }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- public bool DequeueOutgoing()
- {
- OutgoingPacket packet;
- OpenSim.Framework.LocklessQueue<OutgoingPacket> queue;
- TokenBucket bucket;
- bool packetSent = false;
- ThrottleOutPacketTypeFlags emptyCategories = 0;
-
- for (int i = 0; i < THROTTLE_CATEGORY_COUNT; i++)
- {
- bucket = m_throttleCategories[i];
-
- if (m_nextPackets[i] != null)
- {
-
-
-
- OutgoingPacket nextPacket = m_nextPackets[i];
- if (bucket.RemoveTokens(nextPacket.Buffer.DataLength))
- {
-
- m_udpServer.SendPacketFinal(nextPacket);
- m_nextPackets[i] = null;
- packetSent = true;
- }
- }
- else
- {
-
-
- queue = m_packetOutboxes[i];
- if (queue.Dequeue(out packet))
- {
-
-
- if (bucket.RemoveTokens(packet.Buffer.DataLength))
- {
-
- m_udpServer.SendPacketFinal(packet);
- packetSent = true;
- }
- else
- {
-
- m_nextPackets[i] = packet;
- }
-
-
-
- if (queue.Count == 0)
- emptyCategories |= CategoryToFlag(i);
- }
- else
- {
-
-
- emptyCategories |= CategoryToFlag(i);
- }
- }
- }
- if (emptyCategories != 0)
- BeginFireQueueEmpty(emptyCategories);
-
- return packetSent;
- }
-
-
-
-
-
-
-
-
- public void UpdateRoundTrip(float r)
- {
- const float ALPHA = 0.125f;
- const float BETA = 0.25f;
- const float K = 4.0f;
- if (RTTVAR == 0.0f)
- {
-
- SRTT = r;
- RTTVAR = r * 0.5f;
- }
- else
- {
-
- RTTVAR = (1.0f - BETA) * RTTVAR + BETA * Math.Abs(SRTT - r);
- SRTT = (1.0f - ALPHA) * SRTT + ALPHA * r;
- }
- int rto = (int)(SRTT + Math.Max(m_udpServer.TickCountResolution, K * RTTVAR));
-
- rto = Utils.Clamp(rto, m_defaultRTO, m_maxRTO);
- RTO = rto;
-
-
- }
-
-
-
-
- public void BackoffRTO()
- {
-
-
- SRTT = 0.0f;
- RTTVAR = 0.0f;
-
- RTO = Math.Min(RTO * 2, m_maxRTO);
- }
-
-
-
-
-
-
- private void BeginFireQueueEmpty(ThrottleOutPacketTypeFlags categories)
- {
- if (m_nextOnQueueEmpty != 0 && (Environment.TickCount & Int32.MaxValue) >= m_nextOnQueueEmpty)
- {
-
- m_nextOnQueueEmpty = 0;
-
- Util.FireAndForget(FireQueueEmpty, categories);
- }
- }
-
-
-
-
-
-
-
- private void FireQueueEmpty(object o)
- {
- const int MIN_CALLBACK_MS = 30;
- ThrottleOutPacketTypeFlags categories = (ThrottleOutPacketTypeFlags)o;
- QueueEmpty callback = OnQueueEmpty;
-
- int start = Environment.TickCount & Int32.MaxValue;
- if (callback != null)
- {
- try { callback(categories); }
- catch (Exception e) { m_log.Error("[LLUDPCLIENT]: OnQueueEmpty(" + categories + ") threw an exception: " + e.Message, e); }
- }
- m_nextOnQueueEmpty = start + MIN_CALLBACK_MS;
- if (m_nextOnQueueEmpty == 0)
- m_nextOnQueueEmpty = 1;
- }
-
-
-
-
-
-
- private static ThrottleOutPacketTypeFlags CategoryToFlag(int i)
- {
- ThrottleOutPacketType category = (ThrottleOutPacketType)i;
-
- switch (category)
- {
- case ThrottleOutPacketType.Land:
- return ThrottleOutPacketTypeFlags.Land;
- case ThrottleOutPacketType.Wind:
- return ThrottleOutPacketTypeFlags.Wind;
- case ThrottleOutPacketType.Cloud:
- return ThrottleOutPacketTypeFlags.Cloud;
- case ThrottleOutPacketType.Task:
- return ThrottleOutPacketTypeFlags.Task;
- case ThrottleOutPacketType.Texture:
- return ThrottleOutPacketTypeFlags.Texture;
- case ThrottleOutPacketType.Asset:
- return ThrottleOutPacketTypeFlags.Asset;
- case ThrottleOutPacketType.State:
- return ThrottleOutPacketTypeFlags.State;
- default:
- return 0;
- }
- }
- }
- }
|