aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/ClientStack/LindenUDP/LLPacketHandler.cs
diff options
context:
space:
mode:
authorMelanie Thielker2008-07-22 17:58:42 +0000
committerMelanie Thielker2008-07-22 17:58:42 +0000
commitf112cebde2c1bc06108839acac82bc8addd7c506 (patch)
tree7f1e7fabf2fec74171d5982f09d847b47e20d7ca /OpenSim/Region/ClientStack/LindenUDP/LLPacketHandler.cs
parent* refactor: move new inventory service call by user server to OGS1 with all t... (diff)
downloadopensim-SC_OLD-f112cebde2c1bc06108839acac82bc8addd7c506.zip
opensim-SC_OLD-f112cebde2c1bc06108839acac82bc8addd7c506.tar.gz
opensim-SC_OLD-f112cebde2c1bc06108839acac82bc8addd7c506.tar.bz2
opensim-SC_OLD-f112cebde2c1bc06108839acac82bc8addd7c506.tar.xz
Refactor the packet scheduling out of ClientView. Add intelligent
resending, timeouts, packet discarding. Add notification event for packet discarding. Add priority scheduling for packet queues. Add outgoing duplicate detection facility. Correct packet sequencing. Make provisions for automatic server side throttle adjustments (comes in next installment)
Diffstat (limited to 'OpenSim/Region/ClientStack/LindenUDP/LLPacketHandler.cs')
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/LLPacketHandler.cs692
1 files changed, 692 insertions, 0 deletions
diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLPacketHandler.cs b/OpenSim/Region/ClientStack/LindenUDP/LLPacketHandler.cs
new file mode 100644
index 0000000..1ec375f
--- /dev/null
+++ b/OpenSim/Region/ClientStack/LindenUDP/LLPacketHandler.cs
@@ -0,0 +1,692 @@
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 OpenSim 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;
30using System.Collections.Generic;
31using System.Net;
32using System.Net.Sockets;
33using System.Timers;
34using libsecondlife;
35using libsecondlife.Packets;
36using Timer = System.Timers.Timer;
37using OpenSim.Framework;
38
39namespace OpenSim.Region.ClientStack.LindenUDP
40{
41 public delegate void PacketStats(int inPackets, int outPackets, int unAckedBytes);
42 public delegate void PacketDrop(Packet pack, Object id);
43 public delegate bool SynchronizeClientHandler(IScene scene, Packet packet, LLUUID agentID, ThrottleOutPacketType throttlePacketType);
44
45 public interface IPacketHandler
46 {
47 event PacketStats OnPacketStats;
48 event PacketDrop OnPacketDrop;
49 SynchronizeClientHandler SynchronizeClient { set; }
50
51 int PacketsReceived { get; }
52 int PacketsReceivedReported { get; }
53 uint SilenceLimit { get; set; }
54 uint DiscardTimeout { get; set; }
55 uint ResendTimeout { get; set; }
56
57 void InPacket(Packet packet);
58 void ProcessInPacket(Packet packet);
59 void OutPacket(Packet NewPack,
60 ThrottleOutPacketType throttlePacketType);
61 void OutPacket(Packet NewPack,
62 ThrottleOutPacketType throttlePacketType, Object id);
63 LLPacketQueue PacketQueue { get; }
64 void Stop();
65 void Flush();
66 void Clear();
67 ClientInfo GetClientInfo();
68 void SetClientInfo(ClientInfo info);
69 void AddImportantPacket(PacketType type);
70 void RemoveImportantPacket(PacketType type);
71 }
72
73 public class LLPacketHandler : IPacketHandler
74 {
75 // Packet queues
76 //
77 LLPacketQueue m_PacketQueue;
78
79 public LLPacketQueue PacketQueue
80 {
81 get { return m_PacketQueue; }
82 }
83
84 // Timer to run stats and acks on
85 //
86 private Timer m_AckTimer = new Timer(250);
87
88 // A list of the packets we haven't acked yet
89 //
90 private Dictionary<uint,uint> m_PendingAcks = new Dictionary<uint,uint>();
91 // Dictionary of the packets that need acks from the client.
92 //
93 private class AckData
94 {
95 public AckData(Packet packet, Object identifier)
96 {
97 Packet = packet;
98 Identifier = identifier;
99 }
100
101 public Packet Packet;
102 public Object Identifier;
103 }
104 private Dictionary<uint, AckData> m_NeedAck =
105 new Dictionary<uint, AckData>();
106
107 private uint m_ResendTimeout = 1000;
108
109 public uint ResendTimeout
110 {
111 get { return m_ResendTimeout; }
112 set { m_ResendTimeout = value; }
113 }
114
115 private uint m_DiscardTimeout = 8000;
116
117 public uint DiscardTimeout
118 {
119 get { return m_DiscardTimeout; }
120 set { m_DiscardTimeout = value; }
121 }
122
123 private uint m_SilenceLimit = 250;
124
125 public uint SilenceLimit
126 {
127 get { return m_SilenceLimit; }
128 set { m_SilenceLimit = value; }
129 }
130
131 private int m_LastAck = 0;
132
133 // Track duplicated packets. This uses a Dictionary. Both insertion
134 // and lookup are common operations and need to take advantage of
135 // the hashing. Expiration is less common and can be allowed the
136 // time for a linear scan.
137 //
138 private Dictionary<uint, int> m_DupeTracker =
139 new Dictionary<uint, int>();
140 private uint m_DupeTrackerWindow = 30;
141
142 // Values for the SimStatsReporter
143 //
144 private int m_PacketsReceived = 0;
145 private int m_PacketsReceivedReported = 0;
146 private int m_PacketsSent = 0;
147 private int m_PacketsSentReported = 0;
148 private int m_UnackedBytes = 0;
149
150 public int PacketsReceived
151 {
152 get { return m_PacketsReceived; }
153 }
154
155 public int PacketsReceivedReported
156 {
157 get { return m_PacketsReceivedReported; }
158 }
159
160 // The client we are working for
161 //
162 private IClientAPI m_Client;
163
164 // Some events
165 //
166 public event PacketStats OnPacketStats;
167 public event PacketDrop OnPacketDrop;
168
169 private SynchronizeClientHandler m_SynchronizeClient = null;
170
171 public SynchronizeClientHandler SynchronizeClient
172 {
173 set { m_SynchronizeClient = value; }
174 }
175
176 // Packet sequencing
177 //
178 private uint m_Sequence = 0;
179 private object m_SequenceLock = new object();
180 private const int MAX_SEQUENCE = 0xFFFFFF;
181
182 List<PacketType> m_ImportantPackets = new List<PacketType>();
183
184 ////////////////////////////////////////////////////////////////////
185
186 // Constructors
187 //
188 public LLPacketHandler(IClientAPI client)
189 {
190 m_Client = client;
191
192 m_PacketQueue = new LLPacketQueue(client.AgentId);
193
194 m_AckTimer.Elapsed += AckTimerElapsed;
195 m_AckTimer.Start();
196 }
197
198 public void Stop()
199 {
200 m_AckTimer.Stop();
201
202 m_PacketQueue.Enqueue(null);
203 }
204
205 // Send one packet. This actually doesn't send anything, it queues
206 // it. Designed to be fire-and-forget, but there is an optional
207 // notifier.
208 //
209 public void OutPacket(
210 Packet packet, ThrottleOutPacketType throttlePacketType)
211 {
212 OutPacket(packet, throttlePacketType, null);
213 }
214
215 public void OutPacket(
216 Packet packet, ThrottleOutPacketType throttlePacketType,
217 Object id)
218 {
219 // Call the load balancer's hook. If this is not active here
220 // we defer to the sim server this client is actually connected
221 // to. Packet drop notifies will not be triggered in this
222 // configuration!
223 //
224 if ((m_SynchronizeClient != null) && (!m_Client.IsActive))
225 {
226 if (m_SynchronizeClient(m_Client.Scene, packet,
227 m_Client.AgentId, throttlePacketType))
228 return;
229 }
230
231 packet.Header.Sequence = NextPacketSequenceNumber();
232
233 lock(m_NeedAck)
234 {
235 DropResend(id);
236
237 QueuePacket(packet, throttlePacketType, id);
238
239 // We want to see that packet arrive if it's reliable
240 if(packet.Header.Reliable)
241 {
242 m_UnackedBytes += packet.ToBytes().Length;
243 m_NeedAck[packet.Header.Sequence] = new AckData(packet, id);
244 }
245 }
246 }
247
248 private void QueuePacket(
249 Packet packet, ThrottleOutPacketType throttlePacketType,
250 Object id)
251 {
252 // Add acks to outgoing packets
253 //
254 lock(m_PendingAcks)
255 {
256 if(m_PendingAcks.Count > 0)
257 {
258 int count = m_PendingAcks.Count;
259 if(count > 10)
260 count = 10;
261 packet.Header.AckList = new uint[count];
262
263 int i = 0;
264
265 foreach (uint ack in new List<uint>(m_PendingAcks.Keys))
266 {
267 packet.Header.AckList[i] = ack;
268 i++;
269 m_PendingAcks.Remove(ack);
270 if (i >= 10) // That is how much space there is
271 break;
272 }
273 }
274 }
275
276 packet.TickCount = System.Environment.TickCount;
277
278 LLQueItem item = new LLQueItem();
279 item.Packet = packet;
280 item.Incoming = false;
281 item.throttleType = throttlePacketType;
282 item.Identifier = id;
283
284 m_PacketQueue.Enqueue(item);
285 m_PacketsSent++;
286 }
287
288 private void ResendUnacked()
289 {
290 int now = System.Environment.TickCount;
291 int lastAck = m_LastAck;
292
293 // Unless we have received at least one ack, don't bother resending
294 // anything. There may not be a client there, don't clog up the
295 // pipes.
296 //
297 if(lastAck == 0)
298 return;
299
300 lock (m_NeedAck)
301 {
302 // Nothing to do
303 //
304 if(m_NeedAck.Count == 0)
305 return;
306
307 // If we have seen no acks in <SilenceLimit> s but are
308 // waiting for acks, then there may be no one listening.
309 // No need to resend anything. Keep it until it gets stale,
310 // then it will be dropped.
311 //
312 if (((now - lastAck) > m_SilenceLimit) &&
313 m_NeedAck.Count > 0)
314 {
315 return;
316 }
317
318 foreach (AckData data in new List<AckData>(m_NeedAck.Values))
319 {
320 Packet packet = data.Packet;
321
322 // Packets this old get resent
323 //
324 if ((now - packet.TickCount) > m_ResendTimeout)
325 {
326 // Resend the packet. Set the packet's tick count to
327 // now, and keep it marked as resent.
328 //
329 packet.Header.Resent = true;
330 QueuePacket(packet, ThrottleOutPacketType.Resend,
331 data.Identifier);
332 }
333
334 // The discard logic
335 // If the packet is in the queue for <DiscardTimeout> s
336 // without having been processed, then we have clogged
337 // pipes. Most likely, the client is gone
338 // Drop the packets
339 //
340 if ((now - packet.TickCount) > m_DiscardTimeout)
341 {
342 if(!m_ImportantPackets.Contains(packet.Type))
343 m_NeedAck.Remove(packet.Header.Sequence);
344
345 TriggerOnPacketDrop(packet, data.Identifier);
346
347 continue;
348 }
349 }
350 }
351 }
352
353 // Send the pending packet acks to the client
354 // Will send blocks of acks for up to 250 packets
355 //
356 private void SendAcks()
357 {
358 lock (m_PendingAcks)
359 {
360 if (m_PendingAcks.Count == 0)
361 return;
362
363 PacketAckPacket acks = (PacketAckPacket)PacketPool.Instance.GetPacket(PacketType.PacketAck);
364
365 // The case of equality is more common than one might think,
366 // because this function will be called unconditionally when
367 // the counter reaches 250. So there is a good chance another
368 // packet with 250 blocks exists.
369 //
370 if(acks.Packets == null ||
371 acks.Packets.Length != m_PendingAcks.Count)
372 acks.Packets = new PacketAckPacket.PacketsBlock[m_PendingAcks.Count];
373 int i = 0;
374 foreach (uint ack in new List<uint>(m_PendingAcks.Keys))
375 {
376 acks.Packets[i] = new PacketAckPacket.PacketsBlock();
377 acks.Packets[i].ID = ack;
378
379 m_PendingAcks.Remove(ack);
380 i++;
381 }
382
383 acks.Header.Reliable = false;
384 OutPacket(acks, ThrottleOutPacketType.Unknown);
385 }
386 }
387
388 // Queue a packet ack. It will be sent either after 250 acks are
389 // queued, or when the timer fires.
390 //
391 private void AckPacket(Packet packet)
392 {
393 lock (m_PendingAcks)
394 {
395 if(m_PendingAcks.Count < 250)
396 {
397 if(!m_PendingAcks.ContainsKey(packet.Header.Sequence))
398 m_PendingAcks.Add(packet.Header.Sequence,
399 packet.Header.Sequence);
400 return;
401 }
402 }
403
404 SendAcks();
405
406 lock (m_PendingAcks)
407 {
408 // If this is still full we have a truly exceptional
409 // condition (means, can't happen)
410 //
411 if(m_PendingAcks.Count < 250)
412 {
413 if(!m_PendingAcks.ContainsKey(packet.Header.Sequence))
414 m_PendingAcks.Add(packet.Header.Sequence,
415 packet.Header.Sequence);
416 return;
417 }
418 }
419 }
420
421 // When the timer elapses, send the pending acks, trigger resends
422 // and report all the stats.
423 //
424 private void AckTimerElapsed(object sender, ElapsedEventArgs ea)
425 {
426 SendAcks();
427 ResendUnacked();
428 SendPacketStats();
429 }
430
431 // Push out pachet counts for the sim status reporter
432 //
433 private void SendPacketStats()
434 {
435 PacketStats handlerPacketStats = OnPacketStats;
436 if (handlerPacketStats != null)
437 {
438 handlerPacketStats(
439 m_PacketsReceived - m_PacketsReceivedReported,
440 m_PacketsSent - m_PacketsSentReported,
441 m_UnackedBytes);
442
443 m_PacketsReceivedReported = m_PacketsReceived;
444 m_PacketsSentReported = m_PacketsSent;
445 }
446 }
447
448 // We can't keep an unlimited record of dupes. This will prune the
449 // dictionary by age.
450 //
451 private void PruneDupeTracker()
452 {
453 lock (m_DupeTracker)
454 {
455 Dictionary<uint, int> packs =
456 new Dictionary<uint, int>(m_DupeTracker);
457
458 foreach (uint pack in packs.Keys)
459 {
460 if(Util.UnixTimeSinceEpoch() - m_DupeTracker[pack] >
461 m_DupeTrackerWindow)
462 m_DupeTracker.Remove(pack);
463 }
464 }
465 }
466
467 public void InPacket(Packet packet)
468 {
469 if(packet == null)
470 return;
471
472 // If this client is on another partial instance, no need
473 // to handle packets
474 //
475 if(!m_Client.IsActive && packet.Type != PacketType.LogoutRequest)
476 {
477 PacketPool.Instance.ReturnPacket(packet);
478 return;
479 }
480
481 // Any packet can have some packet acks in the header.
482 // Process them here
483 //
484 if(packet.Header.AppendedAcks)
485 {
486 foreach(uint id in packet.Header.AckList)
487 {
488 ProcessAck(id);
489 }
490 }
491
492 // When too many acks are needed to be sent, the client sends
493 // a packet consisting of acks only
494 //
495 if(packet.Type == PacketType.PacketAck)
496 {
497 PacketAckPacket ackPacket = (PacketAckPacket)packet;
498
499 foreach (PacketAckPacket.PacketsBlock block in
500 ackPacket.Packets)
501 {
502 ProcessAck(block.ID);
503 }
504
505 PacketPool.Instance.ReturnPacket(packet);
506 return;
507 }
508 else if(packet.Type == PacketType.StartPingCheck)
509 {
510 StartPingCheckPacket startPing = (StartPingCheckPacket)packet;
511 CompletePingCheckPacket endPing = (CompletePingCheckPacket)PacketPool.Instance.GetPacket(PacketType.CompletePingCheck);
512
513 endPing.PingID.PingID = startPing.PingID.PingID;
514 OutPacket(endPing, ThrottleOutPacketType.Task);
515 }
516 else
517 {
518 LLQueItem item = new LLQueItem();
519 item.Packet = packet;
520 item.Incoming = true;
521 m_PacketQueue.Enqueue(item);
522 }
523 }
524
525 public void ProcessInPacket(Packet packet)
526 {
527 // Always ack the packet!
528 //
529 AckPacket(packet);
530
531 if (packet.Type != PacketType.AgentUpdate)
532 m_PacketsReceived++;
533
534 // Check for duplicate packets.. packets that the client is
535 // resending because it didn't receive our ack
536 //
537 lock (m_DupeTracker)
538 {
539 if (m_DupeTracker.ContainsKey(packet.Header.Sequence))
540 return;
541
542 m_DupeTracker.Add(packet.Header.Sequence,
543 Util.UnixTimeSinceEpoch());
544 }
545
546 m_Client.ProcessInPacket(packet);
547 }
548
549 public void Flush()
550 {
551 m_PacketQueue.Flush();
552 }
553
554 public void Clear()
555 {
556 m_NeedAck.Clear();
557 m_PendingAcks.Clear();
558 m_Sequence += 1000000;
559 }
560
561 private void ProcessAck(uint id)
562 {
563 AckData data;
564 Packet packet;
565
566 lock(m_NeedAck)
567 {
568 if(!m_NeedAck.TryGetValue(id, out data))
569 return;
570
571 packet = data.Packet;
572
573 m_NeedAck.Remove(id);
574 m_UnackedBytes -= packet.ToBytes().Length;
575
576 m_LastAck = System.Environment.TickCount;
577 }
578 }
579
580 // Allocate packet sequence numbers in a threadsave manner
581 //
582 protected uint NextPacketSequenceNumber()
583 {
584 // Set the sequence number
585 uint seq = 1;
586 lock (m_SequenceLock)
587 {
588 if (m_Sequence >= MAX_SEQUENCE)
589 {
590 m_Sequence = 1;
591 }
592 else
593 {
594 m_Sequence++;
595 }
596 seq = m_Sequence;
597 }
598 return seq;
599 }
600
601 public ClientInfo GetClientInfo()
602 {
603 ClientInfo info = new ClientInfo();
604 info.pendingAcks = m_PendingAcks;
605 info.needAck = new Dictionary<uint, byte[]>();
606
607 lock (m_NeedAck)
608 {
609 foreach (uint key in m_NeedAck.Keys)
610 info.needAck.Add(key, m_NeedAck[key].Packet.ToBytes());
611 }
612
613 LLQueItem[] queitems = m_PacketQueue.GetQueueArray();
614
615 for (int i = 0; i < queitems.Length; i++)
616 {
617 if (queitems[i].Incoming == false)
618 info.out_packets.Add(queitems[i].Packet.ToBytes());
619 }
620
621 info.sequence = m_Sequence;
622
623 return info;
624 }
625
626 public void SetClientInfo(ClientInfo info)
627 {
628 m_PendingAcks = info.pendingAcks;
629 m_NeedAck = new Dictionary<uint, AckData>();
630
631 Packet packet = null;
632 int packetEnd = 0;
633 byte[] zero = new byte[3000];
634
635 foreach (uint key in info.needAck.Keys)
636 {
637 byte[] buff = info.needAck[key];
638 packetEnd = buff.Length - 1;
639
640 try
641 {
642 packet = PacketPool.Instance.GetPacket(buff, ref packetEnd, zero);
643 }
644 catch (Exception)
645 {
646 }
647
648 m_NeedAck.Add(key, new AckData(packet, null));
649 }
650
651 m_Sequence = info.sequence;
652 }
653
654 public void AddImportantPacket(PacketType type)
655 {
656 if(m_ImportantPackets.Contains(type))
657 return;
658
659 m_ImportantPackets.Add(type);
660 }
661
662 public void RemoveImportantPacket(PacketType type)
663 {
664 if(!m_ImportantPackets.Contains(type))
665 return;
666
667 m_ImportantPackets.Remove(type);
668 }
669
670 private void DropResend(Object id)
671 {
672 foreach (AckData data in new List<AckData>(m_NeedAck.Values))
673 {
674 if(data.Identifier != null && data.Identifier == id)
675 {
676 m_NeedAck.Remove(data.Packet.Header.Sequence);
677 return;
678 }
679 }
680 }
681
682 private void TriggerOnPacketDrop(Packet packet, Object id)
683 {
684 PacketDrop handlerPacketDrop = OnPacketDrop;
685
686 if(handlerPacketDrop == null)
687 return;
688
689 handlerPacketDrop(packet, id);
690 }
691 }
692}