aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/ClientStack/LindenUDP/LLPacketHandler.cs
diff options
context:
space:
mode:
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}