aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs370
1 files changed, 370 insertions, 0 deletions
diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs
new file mode 100644
index 0000000..ad01135
--- /dev/null
+++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs
@@ -0,0 +1,370 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System;
29using System.Collections.Generic;
30using System.Net;
31using OpenSim.Framework;
32using OpenMetaverse;
33
34namespace OpenSim.Region.ClientStack.LindenUDP
35{
36 public delegate void QueueEmpty(ThrottleOutPacketType category);
37
38 public class LLUDPClient
39 {
40 /// <summary>The number of packet categories to throttle on. If a throttle category is added
41 /// or removed, this number must also change</summary>
42 const int THROTTLE_CATEGORY_COUNT = 7;
43
44 public event QueueEmpty OnQueueEmpty;
45
46 /// <summary>AgentID for this client</summary>
47 public readonly UUID AgentID;
48 /// <summary>The remote address of the connected client</summary>
49 public readonly IPEndPoint RemoteEndPoint;
50 /// <summary>Circuit code that this client is connected on</summary>
51 public readonly uint CircuitCode;
52 /// <summary>Sequence numbers of packets we've received (for duplicate checking)</summary>
53 public readonly IncomingPacketHistoryCollection PacketArchive = new IncomingPacketHistoryCollection(200);
54 /// <summary>Packets we have sent that need to be ACKed by the client</summary>
55 public readonly UnackedPacketCollection NeedAcks = new UnackedPacketCollection();
56 /// <summary>ACKs that are queued up, waiting to be sent to the client</summary>
57 public readonly LocklessQueue<uint> PendingAcks = new LocklessQueue<uint>();
58
59 /// <summary>Reference to the IClientAPI for this client</summary>
60 public LLClientView ClientAPI;
61 /// <summary>Current packet sequence number</summary>
62 public int CurrentSequence;
63 /// <summary>Current ping sequence number</summary>
64 public byte CurrentPingSequence;
65 /// <summary>True when this connection is alive, otherwise false</summary>
66 public bool IsConnected = true;
67 /// <summary>True when this connection is paused, otherwise false</summary>
68 public bool IsPaused = true;
69 /// <summary>Environment.TickCount when the last packet was received for this client</summary>
70 public int TickLastPacketReceived;
71
72 /// <summary>Timer granularity. This is set to the measured resolution of Environment.TickCount</summary>
73 public readonly float G;
74 /// <summary>Smoothed round-trip time. A smoothed average of the round-trip time for sending a
75 /// reliable packet to the client and receiving an ACK</summary>
76 public float SRTT;
77 /// <summary>Round-trip time variance. Measures the consistency of round-trip times</summary>
78 public float RTTVAR;
79 /// <summary>Retransmission timeout. Packets that have not been acknowledged in this number of
80 /// milliseconds or longer will be resent</summary>
81 /// <remarks>Calculated from <seealso cref="SRTT"/> and <seealso cref="RTTVAR"/> using the
82 /// guidelines in RFC 2988</remarks>
83 public int RTO;
84 /// <summary>Number of bytes received since the last acknowledgement was sent out. This is used
85 /// to loosely follow the TCP delayed ACK algorithm in RFC 1122 (4.2.3.2)</summary>
86 public int BytesSinceLastACK;
87
88 /// <summary>Throttle bucket for this agent's connection</summary>
89 private readonly TokenBucket throttle;
90 /// <summary>Throttle buckets for each packet category</summary>
91 private readonly TokenBucket[] throttleCategories;
92 /// <summary>Throttle rate defaults and limits</summary>
93 private readonly ThrottleRates defaultThrottleRates;
94 /// <summary>Outgoing queues for throttled packets</summary>
95 private readonly LocklessQueue<OutgoingPacket>[] packetOutboxes = new LocklessQueue<OutgoingPacket>[THROTTLE_CATEGORY_COUNT];
96 /// <summary>A container that can hold one packet for each outbox, used to store
97 /// dequeued packets that are being held for throttling</summary>
98 private readonly OutgoingPacket[] nextPackets = new OutgoingPacket[THROTTLE_CATEGORY_COUNT];
99 /// <summary>An optimization to store the length of dequeued packets being held
100 /// for throttling. This avoids expensive calls to Packet.Length</summary>
101 private readonly int[] nextPacketLengths = new int[THROTTLE_CATEGORY_COUNT];
102 /// <summary>A reference to the LLUDPServer that is managing this client</summary>
103 private readonly LLUDPServer udpServer;
104
105 public LLUDPClient(LLUDPServer server, ThrottleRates rates, TokenBucket parentThrottle, uint circuitCode, UUID agentID, IPEndPoint remoteEndPoint)
106 {
107 udpServer = server;
108 AgentID = agentID;
109 RemoteEndPoint = remoteEndPoint;
110 CircuitCode = circuitCode;
111 defaultThrottleRates = rates;
112
113 for (int i = 0; i < THROTTLE_CATEGORY_COUNT; i++)
114 packetOutboxes[i] = new LocklessQueue<OutgoingPacket>();
115
116 throttle = new TokenBucket(parentThrottle, 0, 0);
117 throttleCategories = new TokenBucket[THROTTLE_CATEGORY_COUNT];
118 throttleCategories[(int)ThrottleOutPacketType.Resend] = new TokenBucket(throttle, rates.ResendLimit, rates.Resend);
119 throttleCategories[(int)ThrottleOutPacketType.Land] = new TokenBucket(throttle, rates.LandLimit, rates.Land);
120 throttleCategories[(int)ThrottleOutPacketType.Wind] = new TokenBucket(throttle, rates.WindLimit, rates.Wind);
121 throttleCategories[(int)ThrottleOutPacketType.Cloud] = new TokenBucket(throttle, rates.CloudLimit, rates.Cloud);
122 throttleCategories[(int)ThrottleOutPacketType.Task] = new TokenBucket(throttle, rates.TaskLimit, rates.Task);
123 throttleCategories[(int)ThrottleOutPacketType.Texture] = new TokenBucket(throttle, rates.TextureLimit, rates.Texture);
124 throttleCategories[(int)ThrottleOutPacketType.Asset] = new TokenBucket(throttle, rates.AssetLimit, rates.Asset);
125
126 // Set the granularity variable used for retransmission calculations to
127 // the measured resolution of Environment.TickCount
128 G = server.TickCountResolution;
129
130 // Default the retransmission timeout to three seconds
131 RTO = 3000;
132 }
133
134 public void Shutdown()
135 {
136 IsConnected = false;
137 }
138
139 public ClientInfo GetClientInfo()
140 {
141 // TODO: This data structure is wrong in so many ways
142 ClientInfo info = new ClientInfo();
143 info.pendingAcks = new Dictionary<uint, uint>();
144 info.needAck = new Dictionary<uint, byte[]>();
145
146 info.resendThrottle = throttleCategories[(int)ThrottleOutPacketType.Resend].DripRate;
147 info.landThrottle = throttleCategories[(int)ThrottleOutPacketType.Land].DripRate;
148 info.windThrottle = throttleCategories[(int)ThrottleOutPacketType.Wind].DripRate;
149 info.cloudThrottle = throttleCategories[(int)ThrottleOutPacketType.Cloud].DripRate;
150 info.taskThrottle = throttleCategories[(int)ThrottleOutPacketType.Task].DripRate;
151 info.assetThrottle = throttleCategories[(int)ThrottleOutPacketType.Asset].DripRate;
152 info.textureThrottle = throttleCategories[(int)ThrottleOutPacketType.Texture].DripRate;
153 info.totalThrottle = info.resendThrottle + info.landThrottle + info.windThrottle + info.cloudThrottle +
154 info.taskThrottle + info.assetThrottle + info.textureThrottle;
155
156 return info;
157 }
158
159 public void SetClientInfo(ClientInfo info)
160 {
161 }
162
163 public string GetStats()
164 {
165 return string.Format("{0,7} {1,7} {2,7} {3,7} {4,7} {5,7} {6,7} {7,7} {8,7} {9,7}",
166 0,
167 0,
168 0,
169 0,
170 0,
171 0,
172 0,
173 0,
174 0,
175 0);
176 }
177
178 public void SetThrottles(byte[] throttleData)
179 {
180 byte[] adjData;
181 int pos = 0;
182
183 if (!BitConverter.IsLittleEndian)
184 {
185 byte[] newData = new byte[7 * 4];
186 Buffer.BlockCopy(throttleData, 0, newData, 0, 7 * 4);
187
188 for (int i = 0; i < 7; i++)
189 Array.Reverse(newData, i * 4, 4);
190
191 adjData = newData;
192 }
193 else
194 {
195 adjData = throttleData;
196 }
197
198 int resend = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4;
199 int land = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4;
200 int wind = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4;
201 int cloud = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4;
202 int task = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4;
203 int texture = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4;
204 int asset = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f);
205
206 resend = (resend <= defaultThrottleRates.ResendLimit) ? resend : defaultThrottleRates.ResendLimit;
207 land = (land <= defaultThrottleRates.LandLimit) ? land : defaultThrottleRates.LandLimit;
208 wind = (wind <= defaultThrottleRates.WindLimit) ? wind : defaultThrottleRates.WindLimit;
209 cloud = (cloud <= defaultThrottleRates.CloudLimit) ? cloud : defaultThrottleRates.CloudLimit;
210 task = (task <= defaultThrottleRates.TaskLimit) ? task : defaultThrottleRates.TaskLimit;
211 texture = (texture <= defaultThrottleRates.TextureLimit) ? texture : defaultThrottleRates.TextureLimit;
212 asset = (asset <= defaultThrottleRates.AssetLimit) ? asset : defaultThrottleRates.AssetLimit;
213
214 SetThrottle(ThrottleOutPacketType.Resend, resend);
215 SetThrottle(ThrottleOutPacketType.Land, land);
216 SetThrottle(ThrottleOutPacketType.Wind, wind);
217 SetThrottle(ThrottleOutPacketType.Cloud, cloud);
218 SetThrottle(ThrottleOutPacketType.Task, task);
219 SetThrottle(ThrottleOutPacketType.Texture, texture);
220 SetThrottle(ThrottleOutPacketType.Asset, asset);
221 }
222
223 public byte[] GetThrottlesPacked()
224 {
225 byte[] data = new byte[7 * 4];
226 int i = 0;
227
228 Buffer.BlockCopy(Utils.FloatToBytes((float)throttleCategories[(int)ThrottleOutPacketType.Resend].DripRate), 0, data, i, 4); i += 4;
229 Buffer.BlockCopy(Utils.FloatToBytes((float)throttleCategories[(int)ThrottleOutPacketType.Land].DripRate), 0, data, i, 4); i += 4;
230 Buffer.BlockCopy(Utils.FloatToBytes((float)throttleCategories[(int)ThrottleOutPacketType.Wind].DripRate), 0, data, i, 4); i += 4;
231 Buffer.BlockCopy(Utils.FloatToBytes((float)throttleCategories[(int)ThrottleOutPacketType.Cloud].DripRate), 0, data, i, 4); i += 4;
232 Buffer.BlockCopy(Utils.FloatToBytes((float)throttleCategories[(int)ThrottleOutPacketType.Task].DripRate), 0, data, i, 4); i += 4;
233 Buffer.BlockCopy(Utils.FloatToBytes((float)throttleCategories[(int)ThrottleOutPacketType.Texture].DripRate), 0, data, i, 4); i += 4;
234 Buffer.BlockCopy(Utils.FloatToBytes((float)throttleCategories[(int)ThrottleOutPacketType.Asset].DripRate), 0, data, i, 4); i += 4;
235
236 return data;
237 }
238
239 public void SetThrottle(ThrottleOutPacketType category, int rate)
240 {
241 int i = (int)category;
242 if (i >= 0 && i < throttleCategories.Length)
243 {
244 TokenBucket bucket = throttleCategories[(int)category];
245 bucket.MaxBurst = rate;
246 bucket.DripRate = rate;
247 }
248 }
249
250 public bool EnqueueOutgoing(OutgoingPacket packet)
251 {
252 int category = (int)packet.Category;
253
254 if (category >= 0 && category < packetOutboxes.Length)
255 {
256 LocklessQueue<OutgoingPacket> queue = packetOutboxes[category];
257 TokenBucket bucket = throttleCategories[category];
258
259 if (throttleCategories[category].RemoveTokens(packet.Buffer.DataLength))
260 {
261 // Enough tokens were removed from the bucket, the packet will not be queued
262 return false;
263 }
264 else
265 {
266 // Not enough tokens in the bucket, queue this packet
267 queue.Enqueue(packet);
268 return true;
269 }
270 }
271 else
272 {
273 // We don't have a token bucket for this category, so it will not be queued
274 return false;
275 }
276 }
277
278 /// <summary>
279 /// Loops through all of the packet queues for this client and tries to send
280 /// any outgoing packets, obeying the throttling bucket limits
281 /// </summary>
282 /// <remarks>This function is only called from a synchronous loop in the
283 /// UDPServer so we don't need to bother making this thread safe</remarks>
284 /// <returns>True if any packets were sent, otherwise false</returns>
285 public bool DequeueOutgoing()
286 {
287 OutgoingPacket packet;
288 LocklessQueue<OutgoingPacket> queue;
289 TokenBucket bucket;
290 bool packetSent = false;
291
292 for (int i = 0; i < THROTTLE_CATEGORY_COUNT; i++)
293 {
294 bucket = throttleCategories[i];
295
296 if (nextPackets[i] != null)
297 {
298 // This bucket was empty the last time we tried to send a packet,
299 // leaving a dequeued packet still waiting to be sent out. Try to
300 // send it again
301 if (bucket.RemoveTokens(nextPacketLengths[i]))
302 {
303 // Send the packet
304 udpServer.SendPacketFinal(nextPackets[i]);
305 nextPackets[i] = null;
306 packetSent = true;
307 }
308 }
309 else
310 {
311 // No dequeued packet waiting to be sent, try to pull one off
312 // this queue
313 queue = packetOutboxes[i];
314 if (queue.Dequeue(out packet))
315 {
316 // A packet was pulled off the queue. See if we have
317 // enough tokens in the bucket to send it out
318 if (bucket.RemoveTokens(packet.Buffer.DataLength))
319 {
320 // Send the packet
321 udpServer.SendPacketFinal(packet);
322 packetSent = true;
323 }
324 else
325 {
326 // Save the dequeued packet and the length calculation for
327 // the next iteration
328 nextPackets[i] = packet;
329 nextPacketLengths[i] = packet.Buffer.DataLength;
330 }
331 }
332 else
333 {
334 // No packets in this queue. Fire the queue empty callback
335 QueueEmpty callback = OnQueueEmpty;
336 if (callback != null)
337 callback((ThrottleOutPacketType)i);
338 }
339 }
340 }
341
342 return packetSent;
343 }
344
345 public void UpdateRoundTrip(float r)
346 {
347 const float ALPHA = 0.125f;
348 const float BETA = 0.25f;
349 const float K = 4.0f;
350
351 if (RTTVAR == 0.0f)
352 {
353 // First RTT measurement
354 SRTT = r;
355 RTTVAR = r * 0.5f;
356 }
357 else
358 {
359 // Subsequence RTT measurement
360 RTTVAR = (1.0f - BETA) * RTTVAR + BETA * Math.Abs(SRTT - r);
361 SRTT = (1.0f - ALPHA) * SRTT + ALPHA * r;
362 }
363
364 // Always round retransmission timeout up to two seconds
365 RTO = Math.Max(2000, (int)(SRTT + Math.Max(G, K * RTTVAR)));
366 //Logger.Debug("Setting agent " + this.Agent.FullName + "'s RTO to " + RTO + "ms with an RTTVAR of " +
367 // RTTVAR + " based on new RTT of " + r + "ms");
368 }
369 }
370}