diff options
Diffstat (limited to 'OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs')
-rw-r--r-- | OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs | 421 |
1 files changed, 263 insertions, 158 deletions
diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs index 545a0bc..a9f4b2c 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs | |||
@@ -39,6 +39,8 @@ using OpenSim.Framework.Statistics; | |||
39 | using OpenSim.Region.Framework.Scenes; | 39 | using OpenSim.Region.Framework.Scenes; |
40 | using OpenMetaverse; | 40 | using OpenMetaverse; |
41 | 41 | ||
42 | using TokenBucket = OpenSim.Region.ClientStack.LindenUDP.TokenBucket; | ||
43 | |||
42 | namespace OpenSim.Region.ClientStack.LindenUDP | 44 | namespace OpenSim.Region.ClientStack.LindenUDP |
43 | { | 45 | { |
44 | /// <summary> | 46 | /// <summary> |
@@ -89,8 +91,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
89 | /// </summary> | 91 | /// </summary> |
90 | public class LLUDPServer : OpenSimUDPBase | 92 | public class LLUDPServer : OpenSimUDPBase |
91 | { | 93 | { |
94 | /// <summary>Maximum transmission unit, or UDP packet size, for the LLUDP protocol</summary> | ||
95 | public const int MTU = 1400; | ||
96 | |||
92 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | 97 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); |
93 | 98 | ||
99 | /// <summary>The measured resolution of Environment.TickCount</summary> | ||
100 | public readonly float TickCountResolution; | ||
101 | |||
94 | /// <summary>Handlers for incoming packets</summary> | 102 | /// <summary>Handlers for incoming packets</summary> |
95 | //PacketEventDictionary packetEvents = new PacketEventDictionary(); | 103 | //PacketEventDictionary packetEvents = new PacketEventDictionary(); |
96 | /// <summary>Incoming packets that are awaiting handling</summary> | 104 | /// <summary>Incoming packets that are awaiting handling</summary> |
@@ -104,20 +112,35 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
104 | /// <summary>Manages authentication for agent circuits</summary> | 112 | /// <summary>Manages authentication for agent circuits</summary> |
105 | private AgentCircuitManager m_circuitManager; | 113 | private AgentCircuitManager m_circuitManager; |
106 | /// <summary>Reference to the scene this UDP server is attached to</summary> | 114 | /// <summary>Reference to the scene this UDP server is attached to</summary> |
107 | private IScene m_scene; | 115 | private Scene m_scene; |
108 | /// <summary>The X/Y coordinates of the scene this UDP server is attached to</summary> | 116 | /// <summary>The X/Y coordinates of the scene this UDP server is attached to</summary> |
109 | private Location m_location; | 117 | private Location m_location; |
110 | /// <summary>The measured resolution of Environment.TickCount</summary> | ||
111 | private float m_tickCountResolution; | ||
112 | /// <summary>The size of the receive buffer for the UDP socket. This value | 118 | /// <summary>The size of the receive buffer for the UDP socket. This value |
113 | /// is passed up to the operating system and used in the system networking | 119 | /// is passed up to the operating system and used in the system networking |
114 | /// stack. Use zero to leave this value as the default</summary> | 120 | /// stack. Use zero to leave this value as the default</summary> |
115 | private int m_recvBufferSize; | 121 | private int m_recvBufferSize; |
116 | /// <summary>Flag to process packets asynchronously or synchronously</summary> | 122 | /// <summary>Flag to process packets asynchronously or synchronously</summary> |
117 | private bool m_asyncPacketHandling; | 123 | private bool m_asyncPacketHandling; |
124 | /// <summary>Tracks whether or not a packet was sent each round so we know | ||
125 | /// whether or not to sleep</summary> | ||
126 | private bool m_packetSent; | ||
127 | |||
128 | /// <summary>Environment.TickCount of the last time the outgoing packet handler executed</summary> | ||
129 | private int m_tickLastOutgoingPacketHandler; | ||
130 | /// <summary>Keeps track of the number of elapsed milliseconds since the last time the outgoing packet handler looped</summary> | ||
131 | private int m_elapsedMSOutgoingPacketHandler; | ||
132 | /// <summary>Keeps track of the number of 100 millisecond periods elapsed in the outgoing packet handler executed</summary> | ||
133 | private int m_elapsed100MSOutgoingPacketHandler; | ||
134 | /// <summary>Keeps track of the number of 500 millisecond periods elapsed in the outgoing packet handler executed</summary> | ||
135 | private int m_elapsed500MSOutgoingPacketHandler; | ||
136 | |||
137 | /// <summary>Flag to signal when clients should check for resends</summary> | ||
138 | private bool m_resendUnacked; | ||
139 | /// <summary>Flag to signal when clients should send ACKs</summary> | ||
140 | private bool m_sendAcks; | ||
141 | /// <summary>Flag to signal when clients should send pings</summary> | ||
142 | private bool m_sendPing; | ||
118 | 143 | ||
119 | /// <summary>The measured resolution of Environment.TickCount</summary> | ||
120 | public float TickCountResolution { get { return m_tickCountResolution; } } | ||
121 | public Socket Server { get { return null; } } | 144 | public Socket Server { get { return null; } } |
122 | 145 | ||
123 | public LLUDPServer(IPAddress listenIP, ref uint port, int proxyPortOffsetParm, bool allow_alternate_port, IConfigSource configSource, AgentCircuitManager circuitManager) | 146 | public LLUDPServer(IPAddress listenIP, ref uint port, int proxyPortOffsetParm, bool allow_alternate_port, IConfigSource configSource, AgentCircuitManager circuitManager) |
@@ -126,16 +149,17 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
126 | #region Environment.TickCount Measurement | 149 | #region Environment.TickCount Measurement |
127 | 150 | ||
128 | // Measure the resolution of Environment.TickCount | 151 | // Measure the resolution of Environment.TickCount |
129 | m_tickCountResolution = 0f; | 152 | TickCountResolution = 0f; |
130 | for (int i = 0; i < 5; i++) | 153 | for (int i = 0; i < 5; i++) |
131 | { | 154 | { |
132 | int start = Environment.TickCount; | 155 | int start = Environment.TickCount; |
133 | int now = start; | 156 | int now = start; |
134 | while (now == start) | 157 | while (now == start) |
135 | now = Environment.TickCount; | 158 | now = Environment.TickCount; |
136 | m_tickCountResolution += (float)(now - start) * 0.2f; | 159 | TickCountResolution += (float)(now - start) * 0.2f; |
137 | } | 160 | } |
138 | m_log.Info("[LLUDPSERVER]: Average Environment.TickCount resolution: " + TickCountResolution + "ms"); | 161 | m_log.Info("[LLUDPSERVER]: Average Environment.TickCount resolution: " + TickCountResolution + "ms"); |
162 | TickCountResolution = (float)Math.Ceiling(TickCountResolution); | ||
139 | 163 | ||
140 | #endregion Environment.TickCount Measurement | 164 | #endregion Environment.TickCount Measurement |
141 | 165 | ||
@@ -181,15 +205,20 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
181 | 205 | ||
182 | public void AddScene(IScene scene) | 206 | public void AddScene(IScene scene) |
183 | { | 207 | { |
184 | if (m_scene == null) | 208 | if (m_scene != null) |
185 | { | 209 | { |
186 | m_scene = scene; | 210 | m_log.Error("[LLUDPSERVER]: AddScene() called on an LLUDPServer that already has a scene"); |
187 | m_location = new Location(m_scene.RegionInfo.RegionHandle); | 211 | return; |
188 | } | 212 | } |
189 | else | 213 | |
214 | if (!(scene is Scene)) | ||
190 | { | 215 | { |
191 | m_log.Error("[LLUDPSERVER]: AddScene() called on an LLUDPServer that already has a scene"); | 216 | m_log.Error("[LLUDPSERVER]: AddScene() called with an unrecognized scene type " + scene.GetType()); |
217 | return; | ||
192 | } | 218 | } |
219 | |||
220 | m_scene = (Scene)scene; | ||
221 | m_location = new Location(m_scene.RegionInfo.RegionHandle); | ||
193 | } | 222 | } |
194 | 223 | ||
195 | public bool HandlesRegion(Location x) | 224 | public bool HandlesRegion(Location x) |
@@ -267,38 +296,54 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
267 | { | 296 | { |
268 | int dataLength = data.Length; | 297 | int dataLength = data.Length; |
269 | bool doZerocode = (data[0] & Helpers.MSG_ZEROCODED) != 0; | 298 | bool doZerocode = (data[0] & Helpers.MSG_ZEROCODED) != 0; |
299 | bool doCopy = true; | ||
270 | 300 | ||
271 | // Frequency analysis of outgoing packet sizes shows a large clump of packets at each end of the spectrum. | 301 | // Frequency analysis of outgoing packet sizes shows a large clump of packets at each end of the spectrum. |
272 | // The vast majority of packets are less than 200 bytes, although due to asset transfers and packet splitting | 302 | // The vast majority of packets are less than 200 bytes, although due to asset transfers and packet splitting |
273 | // there are a decent number of packets in the 1000-1140 byte range. We allocate one of two sizes of data here | 303 | // there are a decent number of packets in the 1000-1140 byte range. We allocate one of two sizes of data here |
274 | // to accomodate for both common scenarios and provide ample room for ACK appending in both | 304 | // to accomodate for both common scenarios and provide ample room for ACK appending in both |
275 | int bufferSize = (dataLength > 180) ? Packet.MTU : 200; | 305 | int bufferSize = (dataLength > 180) ? LLUDPServer.MTU : 200; |
276 | 306 | ||
277 | UDPPacketBuffer buffer = new UDPPacketBuffer(udpClient.RemoteEndPoint, bufferSize); | 307 | UDPPacketBuffer buffer = new UDPPacketBuffer(udpClient.RemoteEndPoint, bufferSize); |
278 | 308 | ||
279 | // Zerocode if needed | 309 | // Zerocode if needed |
280 | if (doZerocode) | 310 | if (doZerocode) |
281 | { | 311 | { |
282 | try { dataLength = Helpers.ZeroEncode(data, dataLength, buffer.Data); } | 312 | try |
313 | { | ||
314 | dataLength = Helpers.ZeroEncode(data, dataLength, buffer.Data); | ||
315 | doCopy = false; | ||
316 | } | ||
283 | catch (IndexOutOfRangeException) | 317 | catch (IndexOutOfRangeException) |
284 | { | 318 | { |
285 | // The packet grew larger than the bufferSize while zerocoding. | 319 | // The packet grew larger than the bufferSize while zerocoding. |
286 | // Remove the MSG_ZEROCODED flag and send the unencoded data | 320 | // Remove the MSG_ZEROCODED flag and send the unencoded data |
287 | // instead | 321 | // instead |
288 | m_log.Debug("[LLUDPSERVER]: Packet exceeded buffer size during zerocoding for " + type + ". Removing MSG_ZEROCODED flag"); | 322 | m_log.Debug("[LLUDPSERVER]: Packet exceeded buffer size during zerocoding for " + type + ". DataLength=" + dataLength + |
323 | " and BufferLength=" + buffer.Data.Length + ". Removing MSG_ZEROCODED flag"); | ||
289 | data[0] = (byte)(data[0] & ~Helpers.MSG_ZEROCODED); | 324 | data[0] = (byte)(data[0] & ~Helpers.MSG_ZEROCODED); |
290 | Buffer.BlockCopy(data, 0, buffer.Data, 0, dataLength); | ||
291 | } | 325 | } |
292 | } | 326 | } |
293 | else | 327 | |
328 | // If the packet data wasn't already copied during zerocoding, copy it now | ||
329 | if (doCopy) | ||
294 | { | 330 | { |
295 | Buffer.BlockCopy(data, 0, buffer.Data, 0, dataLength); | 331 | if (dataLength <= buffer.Data.Length) |
332 | { | ||
333 | Buffer.BlockCopy(data, 0, buffer.Data, 0, dataLength); | ||
334 | } | ||
335 | else | ||
336 | { | ||
337 | m_log.Error("[LLUDPSERVER]: Packet exceeded buffer size! This could be an indication of packet assembly not obeying the MTU. Type=" + | ||
338 | type + ", DataLength=" + dataLength + ", BufferLength=" + buffer.Data.Length + ". Dropping packet"); | ||
339 | return; | ||
340 | } | ||
296 | } | 341 | } |
342 | |||
297 | buffer.DataLength = dataLength; | 343 | buffer.DataLength = dataLength; |
298 | 344 | ||
299 | #region Queue or Send | 345 | #region Queue or Send |
300 | 346 | ||
301 | // Look up the UDPClient this is going to | ||
302 | OutgoingPacket outgoingPacket = new OutgoingPacket(udpClient, buffer, category); | 347 | OutgoingPacket outgoingPacket = new OutgoingPacket(udpClient, buffer, category); |
303 | 348 | ||
304 | if (!outgoingPacket.Client.EnqueueOutgoing(outgoingPacket)) | 349 | if (!outgoingPacket.Client.EnqueueOutgoing(outgoingPacket)) |
@@ -338,57 +383,54 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
338 | StartPingCheckPacket pc = (StartPingCheckPacket)PacketPool.Instance.GetPacket(PacketType.StartPingCheck); | 383 | StartPingCheckPacket pc = (StartPingCheckPacket)PacketPool.Instance.GetPacket(PacketType.StartPingCheck); |
339 | pc.Header.Reliable = false; | 384 | pc.Header.Reliable = false; |
340 | 385 | ||
341 | OutgoingPacket oldestPacket = udpClient.NeedAcks.GetOldest(); | ||
342 | |||
343 | pc.PingID.PingID = (byte)udpClient.CurrentPingSequence++; | 386 | pc.PingID.PingID = (byte)udpClient.CurrentPingSequence++; |
344 | pc.PingID.OldestUnacked = (oldestPacket != null) ? oldestPacket.SequenceNumber : 0; | 387 | // We *could* get OldestUnacked, but it would hurt performance and not provide any benefit |
388 | pc.PingID.OldestUnacked = 0; | ||
345 | 389 | ||
346 | SendPacket(udpClient, pc, ThrottleOutPacketType.Unknown, false); | 390 | SendPacket(udpClient, pc, ThrottleOutPacketType.Unknown, false); |
347 | } | 391 | } |
348 | 392 | ||
349 | public void ResendUnacked(LLUDPClient udpClient) | 393 | public void ResendUnacked(LLUDPClient udpClient) |
350 | { | 394 | { |
351 | if (udpClient.IsConnected && udpClient.NeedAcks.Count > 0) | 395 | if (!udpClient.IsConnected) |
396 | return; | ||
397 | |||
398 | // Disconnect an agent if no packets are received for some time | ||
399 | //FIXME: Make 60 an .ini setting | ||
400 | if ((Environment.TickCount & Int32.MaxValue) - udpClient.TickLastPacketReceived > 1000 * 60) | ||
352 | { | 401 | { |
353 | // Disconnect an agent if no packets are received for some time | 402 | m_log.Warn("[LLUDPSERVER]: Ack timeout, disconnecting " + udpClient.AgentID); |
354 | //FIXME: Make 60 an .ini setting | ||
355 | if (Environment.TickCount - udpClient.TickLastPacketReceived > 1000 * 60) | ||
356 | { | ||
357 | m_log.Warn("[LLUDPSERVER]: Ack timeout, disconnecting " + udpClient.AgentID); | ||
358 | 403 | ||
359 | RemoveClient(udpClient); | 404 | RemoveClient(udpClient); |
360 | return; | 405 | return; |
361 | } | 406 | } |
362 | 407 | ||
363 | // Get a list of all of the packets that have been sitting unacked longer than udpClient.RTO | 408 | // Get a list of all of the packets that have been sitting unacked longer than udpClient.RTO |
364 | List<OutgoingPacket> expiredPackets = udpClient.NeedAcks.GetExpiredPackets(udpClient.RTO); | 409 | List<OutgoingPacket> expiredPackets = udpClient.NeedAcks.GetExpiredPackets(udpClient.RTO); |
365 | 410 | ||
366 | if (expiredPackets != null) | 411 | if (expiredPackets != null) |
367 | { | 412 | { |
368 | // Resend packets | 413 | m_log.Debug("[LLUDPSERVER]: Resending " + expiredPackets.Count + " packets to " + udpClient.AgentID + ", RTO=" + udpClient.RTO); |
369 | for (int i = 0; i < expiredPackets.Count; i++) | ||
370 | { | ||
371 | OutgoingPacket outgoingPacket = expiredPackets[i]; | ||
372 | 414 | ||
373 | //m_log.DebugFormat("[LLUDPSERVER]: Resending packet #{0} (attempt {1}), {2}ms have passed", | 415 | // Resend packets |
374 | // outgoingPacket.SequenceNumber, outgoingPacket.ResendCount, Environment.TickCount - outgoingPacket.TickCount); | 416 | for (int i = 0; i < expiredPackets.Count; i++) |
417 | { | ||
418 | OutgoingPacket outgoingPacket = expiredPackets[i]; | ||
375 | 419 | ||
376 | // Set the resent flag | 420 | //m_log.DebugFormat("[LLUDPSERVER]: Resending packet #{0} (attempt {1}), {2}ms have passed", |
377 | outgoingPacket.Buffer.Data[0] = (byte)(outgoingPacket.Buffer.Data[0] | Helpers.MSG_RESENT); | 421 | // outgoingPacket.SequenceNumber, outgoingPacket.ResendCount, Environment.TickCount - outgoingPacket.TickCount); |
378 | outgoingPacket.Category = ThrottleOutPacketType.Resend; | ||
379 | 422 | ||
380 | // The TickCount will be set to the current time when the packet | 423 | // Set the resent flag |
381 | // is actually sent out again | 424 | outgoingPacket.Buffer.Data[0] = (byte)(outgoingPacket.Buffer.Data[0] | Helpers.MSG_RESENT); |
382 | outgoingPacket.TickCount = 0; | 425 | outgoingPacket.Category = ThrottleOutPacketType.Resend; |
383 | 426 | ||
384 | // Bump up the resend count on this packet | 427 | // Bump up the resend count on this packet |
385 | Interlocked.Increment(ref outgoingPacket.ResendCount); | 428 | Interlocked.Increment(ref outgoingPacket.ResendCount); |
386 | //Interlocked.Increment(ref Stats.ResentPackets); | 429 | //Interlocked.Increment(ref Stats.ResentPackets); |
387 | 430 | ||
388 | // Requeue or resend the packet | 431 | // Requeue or resend the packet |
389 | if (!outgoingPacket.Client.EnqueueOutgoing(outgoingPacket)) | 432 | if (!outgoingPacket.Client.EnqueueOutgoing(outgoingPacket)) |
390 | SendPacketFinal(outgoingPacket); | 433 | SendPacketFinal(outgoingPacket); |
391 | } | ||
392 | } | 434 | } |
393 | } | 435 | } |
394 | } | 436 | } |
@@ -409,9 +451,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
409 | if (!udpClient.IsConnected) | 451 | if (!udpClient.IsConnected) |
410 | return; | 452 | return; |
411 | 453 | ||
412 | // Keep track of when this packet was sent out (right now) | ||
413 | outgoingPacket.TickCount = Environment.TickCount; | ||
414 | |||
415 | #region ACK Appending | 454 | #region ACK Appending |
416 | 455 | ||
417 | int dataLength = buffer.DataLength; | 456 | int dataLength = buffer.DataLength; |
@@ -464,6 +503,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
464 | 503 | ||
465 | // Put the UDP payload on the wire | 504 | // Put the UDP payload on the wire |
466 | AsyncBeginSend(buffer); | 505 | AsyncBeginSend(buffer); |
506 | |||
507 | // Keep track of when this packet was sent out (right now) | ||
508 | outgoingPacket.TickCount = Environment.TickCount & Int32.MaxValue; | ||
467 | } | 509 | } |
468 | 510 | ||
469 | protected override void PacketReceived(UDPPacketBuffer buffer) | 511 | protected override void PacketReceived(UDPPacketBuffer buffer) |
@@ -506,14 +548,26 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
506 | // UseCircuitCode handling | 548 | // UseCircuitCode handling |
507 | if (packet.Type == PacketType.UseCircuitCode) | 549 | if (packet.Type == PacketType.UseCircuitCode) |
508 | { | 550 | { |
509 | AddNewClient((UseCircuitCodePacket)packet, (IPEndPoint)buffer.RemoteEndPoint); | 551 | Util.FireAndForget( |
552 | delegate(object o) | ||
553 | { | ||
554 | IPEndPoint remoteEndPoint = (IPEndPoint)buffer.RemoteEndPoint; | ||
555 | |||
556 | // Begin the process of adding the client to the simulator | ||
557 | AddNewClient((UseCircuitCodePacket)packet, remoteEndPoint); | ||
558 | |||
559 | // Acknowledge the UseCircuitCode packet | ||
560 | SendAckImmediate(remoteEndPoint, packet.Header.Sequence); | ||
561 | } | ||
562 | ); | ||
563 | return; | ||
510 | } | 564 | } |
511 | 565 | ||
512 | // Determine which agent this packet came from | 566 | // Determine which agent this packet came from |
513 | IClientAPI client; | 567 | IClientAPI client; |
514 | if (!m_scene.ClientManager.TryGetValue(address, out client) || !(client is LLClientView)) | 568 | if (!m_scene.ClientManager.TryGetValue(address, out client) || !(client is LLClientView)) |
515 | { | 569 | { |
516 | m_log.Warn("[LLUDPSERVER]: Received a " + packet.Type + " packet from an unrecognized source: " + address + | 570 | m_log.Debug("[LLUDPSERVER]: Received a " + packet.Type + " packet from an unrecognized source: " + address + |
517 | " in " + m_scene.RegionInfo.RegionName + ", currently tracking " + m_scene.ClientManager.Count + " clients"); | 571 | " in " + m_scene.RegionInfo.RegionName + ", currently tracking " + m_scene.ClientManager.Count + " clients"); |
518 | return; | 572 | return; |
519 | } | 573 | } |
@@ -528,19 +582,16 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
528 | // Stats tracking | 582 | // Stats tracking |
529 | Interlocked.Increment(ref udpClient.PacketsReceived); | 583 | Interlocked.Increment(ref udpClient.PacketsReceived); |
530 | 584 | ||
531 | #region ACK Receiving | 585 | int now = Environment.TickCount & Int32.MaxValue; |
532 | |||
533 | int now = Environment.TickCount; | ||
534 | udpClient.TickLastPacketReceived = now; | 586 | udpClient.TickLastPacketReceived = now; |
535 | 587 | ||
588 | #region ACK Receiving | ||
589 | |||
536 | // Handle appended ACKs | 590 | // Handle appended ACKs |
537 | if (packet.Header.AppendedAcks && packet.Header.AckList != null) | 591 | if (packet.Header.AppendedAcks && packet.Header.AckList != null) |
538 | { | 592 | { |
539 | lock (udpClient.NeedAcks.SyncRoot) | 593 | for (int i = 0; i < packet.Header.AckList.Length; i++) |
540 | { | 594 | udpClient.NeedAcks.Remove(packet.Header.AckList[i], now, packet.Header.Resent); |
541 | for (int i = 0; i < packet.Header.AckList.Length; i++) | ||
542 | AcknowledgePacket(udpClient, packet.Header.AckList[i], now, packet.Header.Resent); | ||
543 | } | ||
544 | } | 595 | } |
545 | 596 | ||
546 | // Handle PacketAck packets | 597 | // Handle PacketAck packets |
@@ -548,11 +599,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
548 | { | 599 | { |
549 | PacketAckPacket ackPacket = (PacketAckPacket)packet; | 600 | PacketAckPacket ackPacket = (PacketAckPacket)packet; |
550 | 601 | ||
551 | lock (udpClient.NeedAcks.SyncRoot) | 602 | for (int i = 0; i < ackPacket.Packets.Length; i++) |
552 | { | 603 | udpClient.NeedAcks.Remove(ackPacket.Packets[i].ID, now, packet.Header.Resent); |
553 | for (int i = 0; i < ackPacket.Packets.Length; i++) | 604 | |
554 | AcknowledgePacket(udpClient, ackPacket.Packets[i].ID, now, packet.Header.Resent); | 605 | // We don't need to do anything else with PacketAck packets |
555 | } | 606 | return; |
556 | } | 607 | } |
557 | 608 | ||
558 | #endregion ACK Receiving | 609 | #endregion ACK Receiving |
@@ -560,20 +611,22 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
560 | #region ACK Sending | 611 | #region ACK Sending |
561 | 612 | ||
562 | if (packet.Header.Reliable) | 613 | if (packet.Header.Reliable) |
614 | { | ||
563 | udpClient.PendingAcks.Enqueue(packet.Header.Sequence); | 615 | udpClient.PendingAcks.Enqueue(packet.Header.Sequence); |
564 | 616 | ||
565 | // This is a somewhat odd sequence of steps to pull the client.BytesSinceLastACK value out, | 617 | // This is a somewhat odd sequence of steps to pull the client.BytesSinceLastACK value out, |
566 | // add the current received bytes to it, test if 2*MTU bytes have been sent, if so remove | 618 | // add the current received bytes to it, test if 2*MTU bytes have been sent, if so remove |
567 | // 2*MTU bytes from the value and send ACKs, and finally add the local value back to | 619 | // 2*MTU bytes from the value and send ACKs, and finally add the local value back to |
568 | // client.BytesSinceLastACK. Lockless thread safety | 620 | // client.BytesSinceLastACK. Lockless thread safety |
569 | int bytesSinceLastACK = Interlocked.Exchange(ref udpClient.BytesSinceLastACK, 0); | 621 | int bytesSinceLastACK = Interlocked.Exchange(ref udpClient.BytesSinceLastACK, 0); |
570 | bytesSinceLastACK += buffer.DataLength; | 622 | bytesSinceLastACK += buffer.DataLength; |
571 | if (bytesSinceLastACK > Packet.MTU * 2) | 623 | if (bytesSinceLastACK > LLUDPServer.MTU * 2) |
572 | { | 624 | { |
573 | bytesSinceLastACK -= Packet.MTU * 2; | 625 | bytesSinceLastACK -= LLUDPServer.MTU * 2; |
574 | SendAcks(udpClient); | 626 | SendAcks(udpClient); |
627 | } | ||
628 | Interlocked.Add(ref udpClient.BytesSinceLastACK, bytesSinceLastACK); | ||
575 | } | 629 | } |
576 | Interlocked.Add(ref udpClient.BytesSinceLastACK, bytesSinceLastACK); | ||
577 | 630 | ||
578 | #endregion ACK Sending | 631 | #endregion ACK Sending |
579 | 632 | ||
@@ -593,16 +646,47 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
593 | 646 | ||
594 | #endregion Incoming Packet Accounting | 647 | #endregion Incoming Packet Accounting |
595 | 648 | ||
596 | // Don't bother clogging up the queue with PacketAck packets that are already handled here | 649 | #region Ping Check Handling |
597 | if (packet.Type != PacketType.PacketAck) | 650 | |
651 | if (packet.Type == PacketType.StartPingCheck) | ||
598 | { | 652 | { |
599 | // Inbox insertion | 653 | // We don't need to do anything else with ping checks |
600 | packetInbox.Enqueue(new IncomingPacket(udpClient, packet)); | 654 | StartPingCheckPacket startPing = (StartPingCheckPacket)packet; |
655 | |||
656 | CompletePingCheckPacket completePing = new CompletePingCheckPacket(); | ||
657 | completePing.PingID.PingID = startPing.PingID.PingID; | ||
658 | SendPacket(udpClient, completePing, ThrottleOutPacketType.Unknown, false); | ||
659 | return; | ||
601 | } | 660 | } |
661 | else if (packet.Type == PacketType.CompletePingCheck) | ||
662 | { | ||
663 | // We don't currently track client ping times | ||
664 | return; | ||
665 | } | ||
666 | |||
667 | #endregion Ping Check Handling | ||
668 | |||
669 | // Inbox insertion | ||
670 | packetInbox.Enqueue(new IncomingPacket(udpClient, packet)); | ||
602 | } | 671 | } |
603 | 672 | ||
604 | protected override void PacketSent(UDPPacketBuffer buffer, int bytesSent) | 673 | private void SendAckImmediate(IPEndPoint remoteEndpoint, uint sequenceNumber) |
605 | { | 674 | { |
675 | PacketAckPacket ack = new PacketAckPacket(); | ||
676 | ack.Header.Reliable = false; | ||
677 | ack.Packets = new PacketAckPacket.PacketsBlock[1]; | ||
678 | ack.Packets[0] = new PacketAckPacket.PacketsBlock(); | ||
679 | ack.Packets[0].ID = sequenceNumber; | ||
680 | |||
681 | byte[] packetData = ack.ToBytes(); | ||
682 | int length = packetData.Length; | ||
683 | |||
684 | UDPPacketBuffer buffer = new UDPPacketBuffer(remoteEndpoint, length); | ||
685 | buffer.DataLength = length; | ||
686 | |||
687 | Buffer.BlockCopy(packetData, 0, buffer.Data, 0, length); | ||
688 | |||
689 | AsyncBeginSend(buffer); | ||
606 | } | 690 | } |
607 | 691 | ||
608 | private bool IsClientAuthorized(UseCircuitCodePacket useCircuitCode, out AuthenticateResponse sessionInfo) | 692 | private bool IsClientAuthorized(UseCircuitCodePacket useCircuitCode, out AuthenticateResponse sessionInfo) |
@@ -672,33 +756,25 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
672 | client.Close(); | 756 | client.Close(); |
673 | } | 757 | } |
674 | 758 | ||
675 | private void AcknowledgePacket(LLUDPClient client, uint ack, int currentTime, bool fromResend) | ||
676 | { | ||
677 | OutgoingPacket ackedPacket; | ||
678 | if (client.NeedAcks.RemoveUnsafe(ack, out ackedPacket) && !fromResend) | ||
679 | { | ||
680 | // Update stats | ||
681 | Interlocked.Add(ref client.UnackedBytes, -ackedPacket.Buffer.DataLength); | ||
682 | |||
683 | // Calculate the round-trip time for this packet and its ACK | ||
684 | int rtt = currentTime - ackedPacket.TickCount; | ||
685 | if (rtt > 0) | ||
686 | client.UpdateRoundTrip(rtt); | ||
687 | } | ||
688 | } | ||
689 | |||
690 | private void IncomingPacketHandler() | 759 | private void IncomingPacketHandler() |
691 | { | 760 | { |
692 | // Set this culture for the thread that incoming packets are received | 761 | // Set this culture for the thread that incoming packets are received |
693 | // on to en-US to avoid number parsing issues | 762 | // on to en-US to avoid number parsing issues |
694 | Culture.SetCurrentCulture(); | 763 | Culture.SetCurrentCulture(); |
695 | 764 | ||
696 | IncomingPacket incomingPacket = null; | ||
697 | |||
698 | while (base.IsRunning) | 765 | while (base.IsRunning) |
699 | { | 766 | { |
700 | if (packetInbox.Dequeue(100, ref incomingPacket)) | 767 | try |
701 | Util.FireAndForget(ProcessInPacket, incomingPacket); | 768 | { |
769 | IncomingPacket incomingPacket = null; | ||
770 | |||
771 | if (packetInbox.Dequeue(100, ref incomingPacket)) | ||
772 | Util.FireAndForget(ProcessInPacket, incomingPacket); | ||
773 | } | ||
774 | catch (Exception ex) | ||
775 | { | ||
776 | m_log.Error("[LLUDPSERVER]: Error in the incoming packet handler loop: " + ex.Message, ex); | ||
777 | } | ||
702 | } | 778 | } |
703 | 779 | ||
704 | if (packetInbox.Count > 0) | 780 | if (packetInbox.Count > 0) |
@@ -712,68 +788,97 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
712 | // on to en-US to avoid number parsing issues | 788 | // on to en-US to avoid number parsing issues |
713 | Culture.SetCurrentCulture(); | 789 | Culture.SetCurrentCulture(); |
714 | 790 | ||
715 | int now = Environment.TickCount; | ||
716 | int elapsedMS = 0; | ||
717 | int elapsed100MS = 0; | ||
718 | int elapsed500MS = 0; | ||
719 | |||
720 | while (base.IsRunning) | 791 | while (base.IsRunning) |
721 | { | 792 | { |
722 | bool resendUnacked = false; | 793 | try |
723 | bool sendAcks = false; | 794 | { |
724 | bool sendPings = false; | 795 | m_packetSent = false; |
725 | bool packetSent = false; | ||
726 | 796 | ||
727 | elapsedMS += Environment.TickCount - now; | 797 | #region Update Timers |
728 | 798 | ||
729 | // Check for pending outgoing resends every 100ms | 799 | m_resendUnacked = false; |
730 | if (elapsedMS >= 100) | 800 | m_sendAcks = false; |
731 | { | 801 | m_sendPing = false; |
732 | resendUnacked = true; | 802 | |
733 | elapsedMS -= 100; | 803 | // Update elapsed time |
734 | ++elapsed100MS; | 804 | int thisTick = Environment.TickCount & Int32.MaxValue; |
805 | if (m_tickLastOutgoingPacketHandler > thisTick) | ||
806 | m_elapsedMSOutgoingPacketHandler += ((Int32.MaxValue - m_tickLastOutgoingPacketHandler) + thisTick); | ||
807 | else | ||
808 | m_elapsedMSOutgoingPacketHandler += (thisTick - m_tickLastOutgoingPacketHandler); | ||
809 | |||
810 | m_tickLastOutgoingPacketHandler = thisTick; | ||
811 | |||
812 | // Check for pending outgoing resends every 100ms | ||
813 | if (m_elapsedMSOutgoingPacketHandler >= 100) | ||
814 | { | ||
815 | m_resendUnacked = true; | ||
816 | m_elapsedMSOutgoingPacketHandler = 0; | ||
817 | m_elapsed100MSOutgoingPacketHandler += 1; | ||
818 | } | ||
819 | |||
820 | // Check for pending outgoing ACKs every 500ms | ||
821 | if (m_elapsed100MSOutgoingPacketHandler >= 5) | ||
822 | { | ||
823 | m_sendAcks = true; | ||
824 | m_elapsed100MSOutgoingPacketHandler = 0; | ||
825 | m_elapsed500MSOutgoingPacketHandler += 1; | ||
826 | } | ||
827 | |||
828 | // Send pings to clients every 5000ms | ||
829 | if (m_elapsed500MSOutgoingPacketHandler >= 10) | ||
830 | { | ||
831 | m_sendPing = true; | ||
832 | m_elapsed500MSOutgoingPacketHandler = 0; | ||
833 | } | ||
834 | |||
835 | #endregion Update Timers | ||
836 | |||
837 | // Handle outgoing packets, resends, acknowledgements, and pings for each | ||
838 | // client. m_packetSent will be set to true if a packet is sent | ||
839 | m_scene.ClientManager.ForEachSync(ClientOutgoingPacketHandler); | ||
840 | |||
841 | // If nothing was sent, sleep for the minimum amount of time before a | ||
842 | // token bucket could get more tokens | ||
843 | if (!m_packetSent) | ||
844 | Thread.Sleep((int)TickCountResolution); | ||
735 | } | 845 | } |
736 | // Check for pending outgoing ACKs every 500ms | 846 | catch (Exception ex) |
737 | if (elapsed100MS >= 5) | ||
738 | { | 847 | { |
739 | sendAcks = true; | 848 | m_log.Error("[LLUDPSERVER]: OutgoingPacketHandler loop threw an exception: " + ex.Message, ex); |
740 | elapsed100MS = 0; | ||
741 | ++elapsed500MS; | ||
742 | } | 849 | } |
743 | // Send pings to clients every 5000ms | 850 | } |
744 | if (elapsed500MS >= 10) | 851 | } |
852 | |||
853 | private void ClientOutgoingPacketHandler(IClientAPI client) | ||
854 | { | ||
855 | try | ||
856 | { | ||
857 | if (client is LLClientView) | ||
745 | { | 858 | { |
746 | sendPings = true; | 859 | LLUDPClient udpClient = ((LLClientView)client).UDPClient; |
747 | elapsed500MS = 0; | ||
748 | } | ||
749 | 860 | ||
750 | m_scene.ClientManager.ForEach( | 861 | if (udpClient.IsConnected) |
751 | delegate(IClientAPI client) | ||
752 | { | 862 | { |
753 | if (client is LLClientView) | 863 | if (m_resendUnacked) |
754 | { | 864 | ResendUnacked(udpClient); |
755 | LLUDPClient udpClient = ((LLClientView)client).UDPClient; | 865 | |
756 | 866 | if (m_sendAcks) | |
757 | if (udpClient.IsConnected) | 867 | SendAcks(udpClient); |
758 | { | 868 | |
759 | if (udpClient.DequeueOutgoing()) | 869 | if (m_sendPing) |
760 | packetSent = true; | 870 | SendPing(udpClient); |
761 | if (resendUnacked) | ||
762 | ResendUnacked(udpClient); | ||
763 | if (sendAcks) | ||
764 | { | ||
765 | SendAcks(udpClient); | ||
766 | udpClient.SendPacketStats(); | ||
767 | } | ||
768 | if (sendPings) | ||
769 | SendPing(udpClient); | ||
770 | } | ||
771 | } | ||
772 | } | ||
773 | ); | ||
774 | 871 | ||
775 | if (!packetSent) | 872 | // Dequeue any outgoing packets that are within the throttle limits |
776 | Thread.Sleep(20); | 873 | if (udpClient.DequeueOutgoing()) |
874 | m_packetSent = true; | ||
875 | } | ||
876 | } | ||
877 | } | ||
878 | catch (Exception ex) | ||
879 | { | ||
880 | m_log.Error("[LLUDPSERVER]: OutgoingPacketHandler iteration for " + client.Name + | ||
881 | " threw an exception: " + ex.Message, ex); | ||
777 | } | 882 | } |
778 | } | 883 | } |
779 | 884 | ||