aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/ClientStack/FunSLUDP/LLPacketHandler.cs
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Region/ClientStack/FunSLUDP/LLPacketHandler.cs')
-rw-r--r--OpenSim/Region/ClientStack/FunSLUDP/LLPacketHandler.cs702
1 files changed, 702 insertions, 0 deletions
diff --git a/OpenSim/Region/ClientStack/FunSLUDP/LLPacketHandler.cs b/OpenSim/Region/ClientStack/FunSLUDP/LLPacketHandler.cs
new file mode 100644
index 0000000..e1a9678
--- /dev/null
+++ b/OpenSim/Region/ClientStack/FunSLUDP/LLPacketHandler.cs
@@ -0,0 +1,702 @@
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 AddAcks(ref packet);
238 QueuePacket(packet, throttlePacketType, id);
239
240 // We want to see that packet arrive if it's reliable
241 if (packet.Header.Reliable)
242 {
243 m_UnackedBytes += packet.ToBytes().Length;
244 m_NeedAck[packet.Header.Sequence] = new AckData(packet, id);
245 }
246 }
247 }
248
249 private void AddAcks(ref Packet packet)
250 {
251 // This packet type has shown to have issues with
252 // acks being appended to the payload, just don't send
253 // any with this packet type until libsl is fixed.
254 //
255 if(packet is libsecondlife.Packets.ViewerEffectPacket)
256 return;
257
258 // Add acks to outgoing packets
259 //
260 if (m_PendingAcks.Count > 0)
261 {
262 int count = m_PendingAcks.Count;
263 if (count > 10)
264 count = 10;
265 packet.Header.AckList = new uint[count];
266 packet.Header.AppendedAcks = true;
267
268 int i = 0;
269
270 foreach (uint ack in new List<uint>(m_PendingAcks.Keys))
271 {
272 packet.Header.AckList[i] = ack;
273 i++;
274 m_PendingAcks.Remove(ack);
275 if (i >= count) // That is how much space there is
276 break;
277 }
278 }
279 }
280
281 private void QueuePacket(
282 Packet packet, ThrottleOutPacketType throttlePacketType,
283 Object id)
284 {
285 packet.TickCount = System.Environment.TickCount;
286
287 LLQueItem item = new LLQueItem();
288 item.Packet = packet;
289 item.Incoming = false;
290 item.throttleType = throttlePacketType;
291 item.Identifier = id;
292
293 m_PacketQueue.Enqueue(item);
294 m_PacketsSent++;
295 }
296
297 private void ResendUnacked()
298 {
299 int now = System.Environment.TickCount;
300 int lastAck = m_LastAck;
301
302 // Unless we have received at least one ack, don't bother resending
303 // anything. There may not be a client there, don't clog up the
304 // pipes.
305 //
306 if (lastAck == 0)
307 return;
308
309 lock (m_NeedAck)
310 {
311 // Nothing to do
312 //
313 if (m_NeedAck.Count == 0)
314 return;
315
316 // If we have seen no acks in <SilenceLimit> s but are
317 // waiting for acks, then there may be no one listening.
318 // No need to resend anything. Keep it until it gets stale,
319 // then it will be dropped.
320 //
321 if ((((now - lastAck) > m_SilenceLimit) &&
322 m_NeedAck.Count > 0) || m_NeedAck.Count == 0)
323 {
324 return;
325 }
326
327 foreach (AckData data in new List<AckData>(m_NeedAck.Values))
328 {
329 Packet packet = data.Packet;
330
331 // Packets this old get resent
332 //
333 if ((now - packet.TickCount) > m_ResendTimeout)
334 {
335 // Resend the packet. Set the packet's tick count to
336 // now, and keep it marked as resent.
337 //
338 packet.Header.Resent = true;
339 QueuePacket(packet, ThrottleOutPacketType.Resend,
340 data.Identifier);
341 }
342
343 // The discard logic
344 // If the packet is in the queue for <DiscardTimeout> s
345 // without having been processed, then we have clogged
346 // pipes. Most likely, the client is gone
347 // Drop the packets
348 //
349 if ((now - packet.TickCount) > m_DiscardTimeout)
350 {
351 if (!m_ImportantPackets.Contains(packet.Type))
352 m_NeedAck.Remove(packet.Header.Sequence);
353
354 TriggerOnPacketDrop(packet, data.Identifier);
355
356 continue;
357 }
358 }
359 }
360 }
361
362 // Send the pending packet acks to the client
363 // Will send blocks of acks for up to 250 packets
364 //
365 private void SendAcks()
366 {
367 lock (m_NeedAck)
368 {
369 if (m_PendingAcks.Count == 0)
370 return;
371
372 PacketAckPacket acks = (PacketAckPacket)PacketPool.Instance.GetPacket(PacketType.PacketAck);
373
374 // The case of equality is more common than one might think,
375 // because this function will be called unconditionally when
376 // the counter reaches 250. So there is a good chance another
377 // packet with 250 blocks exists.
378 //
379 if (acks.Packets == null ||
380 acks.Packets.Length != m_PendingAcks.Count)
381 acks.Packets = new PacketAckPacket.PacketsBlock[m_PendingAcks.Count];
382 int i = 0;
383 foreach (uint ack in new List<uint>(m_PendingAcks.Keys))
384 {
385 acks.Packets[i] = new PacketAckPacket.PacketsBlock();
386 acks.Packets[i].ID = ack;
387
388 m_PendingAcks.Remove(ack);
389 i++;
390 }
391
392 acks.Header.Reliable = false;
393 OutPacket(acks, ThrottleOutPacketType.Unknown);
394 }
395 }
396
397 // Queue a packet ack. It will be sent either after 250 acks are
398 // queued, or when the timer fires.
399 //
400 private void AckPacket(Packet packet)
401 {
402 lock (m_NeedAck)
403 {
404 if (m_PendingAcks.Count < 250)
405 {
406 if (!m_PendingAcks.ContainsKey(packet.Header.Sequence))
407 m_PendingAcks.Add(packet.Header.Sequence,
408 packet.Header.Sequence);
409 return;
410 }
411 }
412
413 SendAcks();
414
415 lock (m_NeedAck)
416 {
417 // If this is still full we have a truly exceptional
418 // condition (means, can't happen)
419 //
420 if (m_PendingAcks.Count < 250)
421 {
422 if (!m_PendingAcks.ContainsKey(packet.Header.Sequence))
423 m_PendingAcks.Add(packet.Header.Sequence,
424 packet.Header.Sequence);
425 return;
426 }
427 }
428 }
429
430 // When the timer elapses, send the pending acks, trigger resends
431 // and report all the stats.
432 //
433 private void AckTimerElapsed(object sender, ElapsedEventArgs ea)
434 {
435 SendAcks();
436 ResendUnacked();
437 SendPacketStats();
438 }
439
440 // Push out pachet counts for the sim status reporter
441 //
442 private void SendPacketStats()
443 {
444 PacketStats handlerPacketStats = OnPacketStats;
445 if (handlerPacketStats != null)
446 {
447 handlerPacketStats(
448 m_PacketsReceived - m_PacketsReceivedReported,
449 m_PacketsSent - m_PacketsSentReported,
450 m_UnackedBytes);
451
452 m_PacketsReceivedReported = m_PacketsReceived;
453 m_PacketsSentReported = m_PacketsSent;
454 }
455 }
456
457 // We can't keep an unlimited record of dupes. This will prune the
458 // dictionary by age.
459 //
460// private void PruneDupeTracker()
461// {
462// lock (m_DupeTracker)
463// {
464// Dictionary<uint, int> packs =
465// new Dictionary<uint, int>(m_DupeTracker);
466//
467// foreach (uint pack in packs.Keys)
468// {
469// if (Util.UnixTimeSinceEpoch() - m_DupeTracker[pack] >
470// m_DupeTrackerWindow)
471// m_DupeTracker.Remove(pack);
472// }
473// }
474// }
475
476 public void InPacket(Packet packet)
477 {
478 if (packet == null)
479 return;
480
481 // If this client is on another partial instance, no need
482 // to handle packets
483 //
484 if (!m_Client.IsActive && packet.Type != PacketType.LogoutRequest)
485 {
486 PacketPool.Instance.ReturnPacket(packet);
487 return;
488 }
489
490 // Any packet can have some packet acks in the header.
491 // Process them here
492 //
493 if (packet.Header.AppendedAcks)
494 {
495 foreach (uint id in packet.Header.AckList)
496 {
497 ProcessAck(id);
498 }
499 }
500
501 // When too many acks are needed to be sent, the client sends
502 // a packet consisting of acks only
503 //
504 if (packet.Type == PacketType.PacketAck)
505 {
506 PacketAckPacket ackPacket = (PacketAckPacket)packet;
507
508 foreach (PacketAckPacket.PacketsBlock block in
509 ackPacket.Packets)
510 {
511 ProcessAck(block.ID);
512 }
513
514 PacketPool.Instance.ReturnPacket(packet);
515 return;
516 }
517 else if (packet.Type == PacketType.StartPingCheck)
518 {
519 StartPingCheckPacket startPing = (StartPingCheckPacket)packet;
520 CompletePingCheckPacket endPing = (CompletePingCheckPacket)PacketPool.Instance.GetPacket(PacketType.CompletePingCheck);
521
522 endPing.PingID.PingID = startPing.PingID.PingID;
523 OutPacket(endPing, ThrottleOutPacketType.Task);
524 }
525 else
526 {
527 LLQueItem item = new LLQueItem();
528 item.Packet = packet;
529 item.Incoming = true;
530 m_PacketQueue.Enqueue(item);
531 }
532 }
533
534 public void ProcessInPacket(Packet packet)
535 {
536 // Always ack the packet!
537 //
538 if (packet.Header.Reliable)
539 AckPacket(packet);
540
541 if (packet.Type != PacketType.AgentUpdate)
542 m_PacketsReceived++;
543
544 // Check for duplicate packets.. packets that the client is
545 // resending because it didn't receive our ack
546 //
547 lock (m_DupeTracker)
548 {
549 if (m_DupeTracker.ContainsKey(packet.Header.Sequence))
550 return;
551
552 m_DupeTracker.Add(packet.Header.Sequence,
553 Util.UnixTimeSinceEpoch());
554 }
555
556 m_Client.ProcessInPacket(packet);
557 }
558
559 public void Flush()
560 {
561 m_PacketQueue.Flush();
562 }
563
564 public void Clear()
565 {
566 m_NeedAck.Clear();
567 m_PendingAcks.Clear();
568 m_Sequence += 1000000;
569 }
570
571 private void ProcessAck(uint id)
572 {
573 AckData data;
574 Packet packet;
575
576 lock (m_NeedAck)
577 {
578 if (!m_NeedAck.TryGetValue(id, out data))
579 return;
580
581 packet = data.Packet;
582
583 m_NeedAck.Remove(id);
584 m_UnackedBytes -= packet.ToBytes().Length;
585
586 m_LastAck = System.Environment.TickCount;
587 }
588 }
589
590 // Allocate packet sequence numbers in a threadsave manner
591 //
592 protected uint NextPacketSequenceNumber()
593 {
594 // Set the sequence number
595 uint seq = 1;
596 lock (m_SequenceLock)
597 {
598 if (m_Sequence >= MAX_SEQUENCE)
599 {
600 m_Sequence = 1;
601 }
602 else
603 {
604 m_Sequence++;
605 }
606 seq = m_Sequence;
607 }
608 return seq;
609 }
610
611 public ClientInfo GetClientInfo()
612 {
613 ClientInfo info = new ClientInfo();
614 info.pendingAcks = m_PendingAcks;
615 info.needAck = new Dictionary<uint, byte[]>();
616
617 lock (m_NeedAck)
618 {
619 foreach (uint key in m_NeedAck.Keys)
620 info.needAck.Add(key, m_NeedAck[key].Packet.ToBytes());
621 }
622
623 LLQueItem[] queitems = m_PacketQueue.GetQueueArray();
624
625 for (int i = 0; i < queitems.Length; i++)
626 {
627 if (queitems[i].Incoming == false)
628 info.out_packets.Add(queitems[i].Packet.ToBytes());
629 }
630
631 info.sequence = m_Sequence;
632
633 return info;
634 }
635
636 public void SetClientInfo(ClientInfo info)
637 {
638 m_PendingAcks = info.pendingAcks;
639 m_NeedAck = new Dictionary<uint, AckData>();
640
641 Packet packet = null;
642 int packetEnd = 0;
643 byte[] zero = new byte[3000];
644
645 foreach (uint key in info.needAck.Keys)
646 {
647 byte[] buff = info.needAck[key];
648 packetEnd = buff.Length - 1;
649
650 try
651 {
652 packet = PacketPool.Instance.GetPacket(buff, ref packetEnd, zero);
653 }
654 catch (Exception)
655 {
656 }
657
658 m_NeedAck.Add(key, new AckData(packet, null));
659 }
660
661 m_Sequence = info.sequence;
662 }
663
664 public void AddImportantPacket(PacketType type)
665 {
666 if (m_ImportantPackets.Contains(type))
667 return;
668
669 m_ImportantPackets.Add(type);
670 }
671
672 public void RemoveImportantPacket(PacketType type)
673 {
674 if (!m_ImportantPackets.Contains(type))
675 return;
676
677 m_ImportantPackets.Remove(type);
678 }
679
680 private void DropResend(Object id)
681 {
682 foreach (AckData data in new List<AckData>(m_NeedAck.Values))
683 {
684 if (data.Identifier != null && data.Identifier == id)
685 {
686 m_NeedAck.Remove(data.Packet.Header.Sequence);
687 return;
688 }
689 }
690 }
691
692 private void TriggerOnPacketDrop(Packet packet, Object id)
693 {
694 PacketDrop handlerPacketDrop = OnPacketDrop;
695
696 if (handlerPacketDrop == null)
697 return;
698
699 handlerPacketDrop(packet, id);
700 }
701 }
702}