aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region
diff options
context:
space:
mode:
authorJohn Hurliman2009-10-13 18:56:54 -0700
committerJohn Hurliman2009-10-13 18:56:54 -0700
commite8c1e69a0dbab1a7db894eeff6b052bbd350a8f5 (patch)
tree6d29a3bae418c1c5a06e3f1b8ad4a59db57a8d9e /OpenSim/Region
parent* Consolidated adding / removing ClientManager IClientAPIs to two places in S... (diff)
downloadopensim-SC-e8c1e69a0dbab1a7db894eeff6b052bbd350a8f5.zip
opensim-SC-e8c1e69a0dbab1a7db894eeff6b052bbd350a8f5.tar.gz
opensim-SC-e8c1e69a0dbab1a7db894eeff6b052bbd350a8f5.tar.bz2
opensim-SC-e8c1e69a0dbab1a7db894eeff6b052bbd350a8f5.tar.xz
* Copied LocklessQueue.cs into OpenSim.Framework and added the .Count property and .Clear() method
* Changed the way the QueueEmpty callback is fired. It will be fired asynchronously as soon as an empty queue is detected (this can happen immediately following a dequeue), and will not be fired again until at least one packet is dequeued from that queue. This will give callbacks advanced notice of an empty queue and prevent callbacks from stacking up while the queue is empty * Added LLUDPClient.IsConnected checks in several places to prevent unwanted network activity after a client disconnects * Prevent LLClientView.Close() from being called twice every disconnect * Removed the packet resend limit and improved the client timeout check
Diffstat (limited to 'OpenSim/Region')
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs11
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/LLImageManager.cs6
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs50
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs107
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs9
5 files changed, 103 insertions, 80 deletions
diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs
index 0acf6e8..ac558ff 100644
--- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs
+++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs
@@ -433,13 +433,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
433 // Remove ourselves from the scene 433 // Remove ourselves from the scene
434 m_scene.RemoveClient(AgentId); 434 m_scene.RemoveClient(AgentId);
435 435
436 //m_log.InfoFormat("[CLIENTVIEW] Memory pre GC {0}", System.GC.GetTotalMemory(false));
437 //GC.Collect();
438 //m_log.InfoFormat("[CLIENTVIEW] Memory post GC {0}", System.GC.GetTotalMemory(true));
439
440 // FIXME: Is this still necessary?
441 //Thread.Sleep(2000);
442
443 // Shut down timers. Thread Context of this method is murky. Lock all timers 436 // Shut down timers. Thread Context of this method is murky. Lock all timers
444 if (m_avatarTerseUpdateTimer.Enabled) 437 if (m_avatarTerseUpdateTimer.Enabled)
445 lock (m_avatarTerseUpdateTimer) 438 lock (m_avatarTerseUpdateTimer)
@@ -461,6 +454,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP
461 454
462 // Disable UDP handling for this client 455 // Disable UDP handling for this client
463 m_udpClient.Shutdown(); 456 m_udpClient.Shutdown();
457
458 //m_log.InfoFormat("[CLIENTVIEW] Memory pre GC {0}", System.GC.GetTotalMemory(false));
459 //GC.Collect();
460 //m_log.InfoFormat("[CLIENTVIEW] Memory post GC {0}", System.GC.GetTotalMemory(true));
464 } 461 }
465 462
466 public void Kick(string message) 463 public void Kick(string message)
diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLImageManager.cs b/OpenSim/Region/ClientStack/LindenUDP/LLImageManager.cs
index 8469ba6..8410ee9 100644
--- a/OpenSim/Region/ClientStack/LindenUDP/LLImageManager.cs
+++ b/OpenSim/Region/ClientStack/LindenUDP/LLImageManager.cs
@@ -213,13 +213,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP
213 213
214 lock (m_syncRoot) 214 lock (m_syncRoot)
215 { 215 {
216
217 if (m_priorityQueue.Count > 0) 216 if (m_priorityQueue.Count > 0)
218 { 217 {
219 try 218 try { image = m_priorityQueue.FindMax(); }
220 {
221 image = m_priorityQueue.FindMax();
222 }
223 catch (Exception) { } 219 catch (Exception) { }
224 } 220 }
225 } 221 }
diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs
index e5b2594..b27d8d6 100644
--- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs
+++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs
@@ -80,7 +80,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
80 /// <summary>Packets we have sent that need to be ACKed by the client</summary> 80 /// <summary>Packets we have sent that need to be ACKed by the client</summary>
81 public readonly UnackedPacketCollection NeedAcks = new UnackedPacketCollection(); 81 public readonly UnackedPacketCollection NeedAcks = new UnackedPacketCollection();
82 /// <summary>ACKs that are queued up, waiting to be sent to the client</summary> 82 /// <summary>ACKs that are queued up, waiting to be sent to the client</summary>
83 public readonly LocklessQueue<uint> PendingAcks = new LocklessQueue<uint>(); 83 public readonly OpenSim.Framework.LocklessQueue<uint> PendingAcks = new OpenSim.Framework.LocklessQueue<uint>();
84 84
85 /// <summary>Current packet sequence number</summary> 85 /// <summary>Current packet sequence number</summary>
86 public int CurrentSequence; 86 public int CurrentSequence;
@@ -127,13 +127,16 @@ namespace OpenSim.Region.ClientStack.LindenUDP
127 /// <summary>Throttle rate defaults and limits</summary> 127 /// <summary>Throttle rate defaults and limits</summary>
128 private readonly ThrottleRates defaultThrottleRates; 128 private readonly ThrottleRates defaultThrottleRates;
129 /// <summary>Outgoing queues for throttled packets</summary> 129 /// <summary>Outgoing queues for throttled packets</summary>
130 private readonly LocklessQueue<OutgoingPacket>[] packetOutboxes = new LocklessQueue<OutgoingPacket>[THROTTLE_CATEGORY_COUNT]; 130 private readonly OpenSim.Framework.LocklessQueue<OutgoingPacket>[] packetOutboxes = new OpenSim.Framework.LocklessQueue<OutgoingPacket>[THROTTLE_CATEGORY_COUNT];
131 /// <summary>A container that can hold one packet for each outbox, used to store 131 /// <summary>A container that can hold one packet for each outbox, used to store
132 /// dequeued packets that are being held for throttling</summary> 132 /// dequeued packets that are being held for throttling</summary>
133 private readonly OutgoingPacket[] nextPackets = new OutgoingPacket[THROTTLE_CATEGORY_COUNT]; 133 private readonly OutgoingPacket[] nextPackets = new OutgoingPacket[THROTTLE_CATEGORY_COUNT];
134 /// <summary>An optimization to store the length of dequeued packets being held 134 /// <summary>An optimization to store the length of dequeued packets being held
135 /// for throttling. This avoids expensive calls to Packet.Length</summary> 135 /// for throttling. This avoids expensive calls to Packet.Length</summary>
136 private readonly int[] nextPacketLengths = new int[THROTTLE_CATEGORY_COUNT]; 136 private readonly int[] nextPacketLengths = new int[THROTTLE_CATEGORY_COUNT];
137 /// <summary>Flags to prevent queue empty callbacks from repeatedly firing
138 /// before the callbacks have a chance to put packets in the queue</summary>
139 private readonly bool[] queueEmptySent = new bool[THROTTLE_CATEGORY_COUNT];
137 /// <summary>A reference to the LLUDPServer that is managing this client</summary> 140 /// <summary>A reference to the LLUDPServer that is managing this client</summary>
138 private readonly LLUDPServer udpServer; 141 private readonly LLUDPServer udpServer;
139 142
@@ -156,7 +159,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
156 defaultThrottleRates = rates; 159 defaultThrottleRates = rates;
157 160
158 for (int i = 0; i < THROTTLE_CATEGORY_COUNT; i++) 161 for (int i = 0; i < THROTTLE_CATEGORY_COUNT; i++)
159 packetOutboxes[i] = new LocklessQueue<OutgoingPacket>(); 162 packetOutboxes[i] = new OpenSim.Framework.LocklessQueue<OutgoingPacket>();
160 163
161 throttle = new TokenBucket(parentThrottle, 0, 0); 164 throttle = new TokenBucket(parentThrottle, 0, 0);
162 throttleCategories = new TokenBucket[THROTTLE_CATEGORY_COUNT]; 165 throttleCategories = new TokenBucket[THROTTLE_CATEGORY_COUNT];
@@ -182,6 +185,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP
182 public void Shutdown() 185 public void Shutdown()
183 { 186 {
184 IsConnected = false; 187 IsConnected = false;
188 NeedAcks.Clear();
189 for (int i = 0; i < THROTTLE_CATEGORY_COUNT; i++)
190 {
191 packetOutboxes[i].Clear();
192 nextPackets[i] = null;
193 }
194 OnPacketStats = null;
195 OnQueueEmpty = null;
185 } 196 }
186 197
187 /// <summary> 198 /// <summary>
@@ -322,7 +333,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
322 333
323 if (category >= 0 && category < packetOutboxes.Length) 334 if (category >= 0 && category < packetOutboxes.Length)
324 { 335 {
325 LocklessQueue<OutgoingPacket> queue = packetOutboxes[category]; 336 OpenSim.Framework.LocklessQueue<OutgoingPacket> queue = packetOutboxes[category];
326 TokenBucket bucket = throttleCategories[category]; 337 TokenBucket bucket = throttleCategories[category];
327 338
328 if (throttleCategories[category].RemoveTokens(packet.Buffer.DataLength)) 339 if (throttleCategories[category].RemoveTokens(packet.Buffer.DataLength))
@@ -354,7 +365,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
354 public bool DequeueOutgoing() 365 public bool DequeueOutgoing()
355 { 366 {
356 OutgoingPacket packet; 367 OutgoingPacket packet;
357 LocklessQueue<OutgoingPacket> queue; 368 OpenSim.Framework.LocklessQueue<OutgoingPacket> queue;
358 TokenBucket bucket; 369 TokenBucket bucket;
359 bool packetSent = false; 370 bool packetSent = false;
360 371
@@ -382,6 +393,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP
382 queue = packetOutboxes[i]; 393 queue = packetOutboxes[i];
383 if (queue.Dequeue(out packet)) 394 if (queue.Dequeue(out packet))
384 { 395 {
396 // Reset the flag for firing this queue's OnQueueEmpty callback
397 // now that we have dequeued a packet
398 queueEmptySent[i] = false;
399
385 // A packet was pulled off the queue. See if we have 400 // A packet was pulled off the queue. See if we have
386 // enough tokens in the bucket to send it out 401 // enough tokens in the bucket to send it out
387 if (bucket.RemoveTokens(packet.Buffer.DataLength)) 402 if (bucket.RemoveTokens(packet.Buffer.DataLength))
@@ -397,13 +412,18 @@ namespace OpenSim.Region.ClientStack.LindenUDP
397 nextPackets[i] = packet; 412 nextPackets[i] = packet;
398 nextPacketLengths[i] = packet.Buffer.DataLength; 413 nextPacketLengths[i] = packet.Buffer.DataLength;
399 } 414 }
415
416 // If the queue is empty after this dequeue, fire the queue
417 // empty callback now so it has a chance to fill before we
418 // get back here
419 if (queue.Count == 0)
420 FireQueueEmpty(i);
400 } 421 }
401 else 422 else
402 { 423 {
403 // No packets in this queue. Fire the queue empty callback 424 // No packets in this queue. Fire the queue empty callback
404 QueueEmpty callback = OnQueueEmpty; 425 // if it has not been called recently
405 if (callback != null) 426 FireQueueEmpty(i);
406 callback((ThrottleOutPacketType)i);
407 } 427 }
408 } 428 }
409 } 429 }
@@ -432,8 +452,20 @@ namespace OpenSim.Region.ClientStack.LindenUDP
432 452
433 // Always round retransmission timeout up to two seconds 453 // Always round retransmission timeout up to two seconds
434 RTO = Math.Max(2000, (int)(SRTT + Math.Max(G, K * RTTVAR))); 454 RTO = Math.Max(2000, (int)(SRTT + Math.Max(G, K * RTTVAR)));
435 //Logger.Debug("Setting agent " + this.Agent.FullName + "'s RTO to " + RTO + "ms with an RTTVAR of " + 455 //m_log.Debug("[LLUDPCLIENT]: Setting agent " + this.Agent.FullName + "'s RTO to " + RTO + "ms with an RTTVAR of " +
436 // RTTVAR + " based on new RTT of " + r + "ms"); 456 // RTTVAR + " based on new RTT of " + r + "ms");
437 } 457 }
458
459 private void FireQueueEmpty(int queueIndex)
460 {
461 if (!queueEmptySent[queueIndex])
462 {
463 queueEmptySent[queueIndex] = true;
464
465 QueueEmpty callback = OnQueueEmpty;
466 if (callback != null)
467 Util.FireAndForget(delegate(object o) { callback((ThrottleOutPacketType)(int)o); }, queueIndex);
468 }
469 }
438 } 470 }
439} 471}
diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs
index 8689af3..57fee59 100644
--- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs
+++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs
@@ -332,8 +332,19 @@ namespace OpenSim.Region.ClientStack.LindenUDP
332 332
333 public void ResendUnacked(LLUDPClient udpClient) 333 public void ResendUnacked(LLUDPClient udpClient)
334 { 334 {
335 if (udpClient.NeedAcks.Count > 0) 335 if (udpClient.IsConnected && udpClient.NeedAcks.Count > 0)
336 { 336 {
337 // Disconnect an agent if no packets are received for some time
338 //FIXME: Make 60 an .ini setting
339 if (Environment.TickCount - udpClient.TickLastPacketReceived > 1000 * 60)
340 {
341 m_log.Warn("[LLUDPSERVER]: Ack timeout, disconnecting " + udpClient.AgentID);
342
343 RemoveClient(udpClient);
344 return;
345 }
346
347 // Get a list of all of the packets that have been sitting unacked longer than udpClient.RTO
337 List<OutgoingPacket> expiredPackets = udpClient.NeedAcks.GetExpiredPackets(udpClient.RTO); 348 List<OutgoingPacket> expiredPackets = udpClient.NeedAcks.GetExpiredPackets(udpClient.RTO);
338 349
339 if (expiredPackets != null) 350 if (expiredPackets != null)
@@ -343,48 +354,24 @@ namespace OpenSim.Region.ClientStack.LindenUDP
343 { 354 {
344 OutgoingPacket outgoingPacket = expiredPackets[i]; 355 OutgoingPacket outgoingPacket = expiredPackets[i];
345 356
346 // FIXME: Make this an .ini setting 357 //m_log.DebugFormat("[LLUDPSERVER]: Resending packet #{0} (attempt {1}), {2}ms have passed",
347 if (outgoingPacket.ResendCount < 3) 358 // outgoingPacket.SequenceNumber, outgoingPacket.ResendCount, Environment.TickCount - outgoingPacket.TickCount);
348 {
349 //Logger.Debug(String.Format("Resending packet #{0} (attempt {1}), {2}ms have passed",
350 // outgoingPacket.SequenceNumber, outgoingPacket.ResendCount, Environment.TickCount - outgoingPacket.TickCount));
351 359
352 // Set the resent flag 360 // Set the resent flag
353 outgoingPacket.Buffer.Data[0] = (byte)(outgoingPacket.Buffer.Data[0] | Helpers.MSG_RESENT); 361 outgoingPacket.Buffer.Data[0] = (byte)(outgoingPacket.Buffer.Data[0] | Helpers.MSG_RESENT);
354 outgoingPacket.Category = ThrottleOutPacketType.Resend; 362 outgoingPacket.Category = ThrottleOutPacketType.Resend;
355 363
356 // The TickCount will be set to the current time when the packet 364 // The TickCount will be set to the current time when the packet
357 // is actually sent out again 365 // is actually sent out again
358 outgoingPacket.TickCount = 0; 366 outgoingPacket.TickCount = 0;
359 367
360 // Bump up the resend count on this packet 368 // Bump up the resend count on this packet
361 Interlocked.Increment(ref outgoingPacket.ResendCount); 369 Interlocked.Increment(ref outgoingPacket.ResendCount);
362 //Interlocked.Increment(ref Stats.ResentPackets); 370 //Interlocked.Increment(ref Stats.ResentPackets);
363 371
364 // Queue or (re)send the packet 372 // Requeue or resend the packet
365 if (!outgoingPacket.Client.EnqueueOutgoing(outgoingPacket)) 373 if (!outgoingPacket.Client.EnqueueOutgoing(outgoingPacket))
366 SendPacketFinal(outgoingPacket); 374 SendPacketFinal(outgoingPacket);
367 }
368 else
369 {
370 m_log.DebugFormat("[LLUDPSERVER]: Dropping packet #{0} for agent {1} after {2} failed attempts",
371 outgoingPacket.SequenceNumber, outgoingPacket.Client.RemoteEndPoint, outgoingPacket.ResendCount);
372
373 lock (udpClient.NeedAcks.SyncRoot)
374 udpClient.NeedAcks.RemoveUnsafe(outgoingPacket.SequenceNumber);
375
376 //Interlocked.Increment(ref Stats.DroppedPackets);
377
378 // Disconnect an agent if no packets are received for some time
379 //FIXME: Make 60 an .ini setting
380 if (Environment.TickCount - udpClient.TickLastPacketReceived > 1000 * 60)
381 {
382 m_log.Warn("[LLUDPSERVER]: Ack timeout, disconnecting " + udpClient.AgentID);
383
384 RemoveClient(udpClient);
385 return;
386 }
387 }
388 } 375 }
389 } 376 }
390 } 377 }
@@ -403,6 +390,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP
403 bool isReliable = (flags & Helpers.MSG_RELIABLE) != 0; 390 bool isReliable = (flags & Helpers.MSG_RELIABLE) != 0;
404 LLUDPClient udpClient = outgoingPacket.Client; 391 LLUDPClient udpClient = outgoingPacket.Client;
405 392
393 if (!udpClient.IsConnected)
394 return;
395
406 // Keep track of when this packet was sent out (right now) 396 // Keep track of when this packet was sent out (right now)
407 outgoingPacket.TickCount = Environment.TickCount; 397 outgoingPacket.TickCount = Environment.TickCount;
408 398
@@ -481,14 +471,15 @@ namespace OpenSim.Region.ClientStack.LindenUDP
481 } 471 }
482 catch (MalformedDataException) 472 catch (MalformedDataException)
483 { 473 {
484 m_log.ErrorFormat("[LLUDPSERVER]: Malformed data, cannot parse packet:\n{0}", 474 m_log.ErrorFormat("[LLUDPSERVER]: Malformed data, cannot parse packet from {0}:\n{1}",
485 Utils.BytesToHexString(buffer.Data, buffer.DataLength, null)); 475 buffer.RemoteEndPoint, Utils.BytesToHexString(buffer.Data, buffer.DataLength, null));
486 } 476 }
487 477
488 // Fail-safe check 478 // Fail-safe check
489 if (packet == null) 479 if (packet == null)
490 { 480 {
491 m_log.Warn("[LLUDPSERVER]: Couldn't build a message from the incoming data"); 481 m_log.Warn("[LLUDPSERVER]: Couldn't build a message from incoming data " + buffer.DataLength +
482 " bytes long from " + buffer.RemoteEndPoint);
492 return; 483 return;
493 } 484 }
494 485
@@ -513,6 +504,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP
513 504
514 udpClient = ((LLClientView)client).UDPClient; 505 udpClient = ((LLClientView)client).UDPClient;
515 506
507 if (!udpClient.IsConnected)
508 return;
509
516 #endregion Packet to Client Mapping 510 #endregion Packet to Client Mapping
517 511
518 // Stats tracking 512 // Stats tracking
@@ -643,7 +637,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
643 // Create the LLClientView 637 // Create the LLClientView
644 LLClientView client = new LLClientView(remoteEndPoint, m_scene, this, udpClient, sessionInfo, agentID, sessionID, circuitCode); 638 LLClientView client = new LLClientView(remoteEndPoint, m_scene, this, udpClient, sessionInfo, agentID, sessionID, circuitCode);
645 client.OnLogout += LogoutHandler; 639 client.OnLogout += LogoutHandler;
646 client.OnConnectionClosed += ConnectionClosedHandler;
647 640
648 // Start the IClientAPI 641 // Start the IClientAPI
649 client.Start(); 642 client.Start();
@@ -745,17 +738,20 @@ namespace OpenSim.Region.ClientStack.LindenUDP
745 { 738 {
746 LLUDPClient udpClient = ((LLClientView)client).UDPClient; 739 LLUDPClient udpClient = ((LLClientView)client).UDPClient;
747 740
748 if (udpClient.DequeueOutgoing()) 741 if (udpClient.IsConnected)
749 packetSent = true;
750 if (resendUnacked)
751 ResendUnacked(udpClient);
752 if (sendAcks)
753 { 742 {
754 SendAcks(udpClient); 743 if (udpClient.DequeueOutgoing())
755 udpClient.SendPacketStats(); 744 packetSent = true;
745 if (resendUnacked)
746 ResendUnacked(udpClient);
747 if (sendAcks)
748 {
749 SendAcks(udpClient);
750 udpClient.SendPacketStats();
751 }
752 if (sendPings)
753 SendPing(udpClient);
756 } 754 }
757 if (sendPings)
758 SendPing(udpClient);
759 } 755 }
760 } 756 }
761 ); 757 );
@@ -808,14 +804,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
808 804
809 private void LogoutHandler(IClientAPI client) 805 private void LogoutHandler(IClientAPI client)
810 { 806 {
811 client.OnLogout -= LogoutHandler;
812 client.SendLogoutPacket(); 807 client.SendLogoutPacket();
813 } 808 }
814
815 private void ConnectionClosedHandler(IClientAPI client)
816 {
817 client.OnConnectionClosed -= ConnectionClosedHandler;
818 RemoveClient(((LLClientView)client).UDPClient);
819 }
820 } 809 }
821} 810}
diff --git a/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs b/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs
index 195ca57..f3242c1 100644
--- a/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs
+++ b/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs
@@ -103,6 +103,15 @@ namespace OpenSim.Region.ClientStack.LindenUDP
103 } 103 }
104 104
105 /// <summary> 105 /// <summary>
106 /// Removes all elements from the collection
107 /// </summary>
108 public void Clear()
109 {
110 lock (SyncRoot)
111 packets.Clear();
112 }
113
114 /// <summary>
106 /// Gets the packet with the lowest sequence number 115 /// Gets the packet with the lowest sequence number
107 /// </summary> 116 /// </summary>
108 /// <returns>The packet with the lowest sequence number, or null if the 117 /// <returns>The packet with the lowest sequence number, or null if the