diff options
Diffstat (limited to '')
-rw-r--r-- | OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs | 415 |
1 files changed, 214 insertions, 201 deletions
diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs index c356f02..890f701 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs | |||
@@ -96,7 +96,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
96 | /// <summary>Incoming packets that are awaiting handling</summary> | 96 | /// <summary>Incoming packets that are awaiting handling</summary> |
97 | private OpenMetaverse.BlockingQueue<IncomingPacket> packetInbox = new OpenMetaverse.BlockingQueue<IncomingPacket>(); | 97 | private OpenMetaverse.BlockingQueue<IncomingPacket> packetInbox = new OpenMetaverse.BlockingQueue<IncomingPacket>(); |
98 | /// <summary></summary> | 98 | /// <summary></summary> |
99 | private UDPClientCollection clients = new UDPClientCollection(); | 99 | //private UDPClientCollection m_clients = new UDPClientCollection(); |
100 | /// <summary>Bandwidth throttle for this UDP server</summary> | 100 | /// <summary>Bandwidth throttle for this UDP server</summary> |
101 | private TokenBucket m_throttle; | 101 | private TokenBucket m_throttle; |
102 | /// <summary>Bandwidth throttle rates for this UDP server</summary> | 102 | /// <summary>Bandwidth throttle rates for this UDP server</summary> |
@@ -109,13 +109,17 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
109 | private Location m_location; | 109 | private Location m_location; |
110 | /// <summary>The measured resolution of Environment.TickCount</summary> | 110 | /// <summary>The measured resolution of Environment.TickCount</summary> |
111 | private float m_tickCountResolution; | 111 | private float m_tickCountResolution; |
112 | /// <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 | ||
114 | /// stack. Use zero to leave this value as the default</summary> | ||
115 | private int m_recvBufferSize; | ||
112 | 116 | ||
113 | /// <summary>The measured resolution of Environment.TickCount</summary> | 117 | /// <summary>The measured resolution of Environment.TickCount</summary> |
114 | public float TickCountResolution { get { return m_tickCountResolution; } } | 118 | public float TickCountResolution { get { return m_tickCountResolution; } } |
115 | public Socket Server { get { return null; } } | 119 | public Socket Server { get { return null; } } |
116 | 120 | ||
117 | public LLUDPServer(IPAddress listenIP, ref uint port, int proxyPortOffsetParm, bool allow_alternate_port, IConfigSource configSource, AgentCircuitManager circuitManager) | 121 | public LLUDPServer(IPAddress listenIP, ref uint port, int proxyPortOffsetParm, bool allow_alternate_port, IConfigSource configSource, AgentCircuitManager circuitManager) |
118 | : base((int)port) | 122 | : base(listenIP, (int)port) |
119 | { | 123 | { |
120 | #region Environment.TickCount Measurement | 124 | #region Environment.TickCount Measurement |
121 | 125 | ||
@@ -134,18 +138,25 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
134 | #endregion Environment.TickCount Measurement | 138 | #endregion Environment.TickCount Measurement |
135 | 139 | ||
136 | m_circuitManager = circuitManager; | 140 | m_circuitManager = circuitManager; |
141 | int sceneThrottleBps = 0; | ||
137 | 142 | ||
138 | // TODO: Config support for throttling the entire connection | 143 | IConfig config = configSource.Configs["ClientStack.LindenUDP"]; |
139 | m_throttle = new TokenBucket(null, 0, 0); | 144 | if (config != null) |
145 | { | ||
146 | m_recvBufferSize = config.GetInt("client_socket_rcvbuf_size", 0); | ||
147 | sceneThrottleBps = config.GetInt("scene_throttle_max_bps", 0); | ||
148 | } | ||
149 | |||
150 | m_throttle = new TokenBucket(null, sceneThrottleBps, sceneThrottleBps); | ||
140 | m_throttleRates = new ThrottleRates(configSource); | 151 | m_throttleRates = new ThrottleRates(configSource); |
141 | } | 152 | } |
142 | 153 | ||
143 | public new void Start() | 154 | public void Start() |
144 | { | 155 | { |
145 | if (m_scene == null) | 156 | if (m_scene == null) |
146 | throw new InvalidOperationException("Cannot LLUDPServer.Start() without an IScene reference"); | 157 | throw new InvalidOperationException("[LLUDPSERVER]: Cannot LLUDPServer.Start() without an IScene reference"); |
147 | 158 | ||
148 | base.Start(); | 159 | base.Start(m_recvBufferSize); |
149 | 160 | ||
150 | // Start the incoming packet processing thread | 161 | // Start the incoming packet processing thread |
151 | Thread incomingThread = new Thread(IncomingPacketHandler); | 162 | Thread incomingThread = new Thread(IncomingPacketHandler); |
@@ -181,24 +192,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
181 | return x == m_location; | 192 | return x == m_location; |
182 | } | 193 | } |
183 | 194 | ||
184 | public void RemoveClient(IClientAPI client) | ||
185 | { | ||
186 | m_scene.ClientManager.Remove(client.CircuitCode); | ||
187 | client.Close(false); | ||
188 | |||
189 | LLUDPClient udpClient; | ||
190 | if (clients.TryGetValue(client.AgentId, out udpClient)) | ||
191 | { | ||
192 | m_log.Debug("[LLUDPSERVER]: Removing LLUDPClient for " + client.Name + " in " + m_scene.RegionInfo.RegionName); | ||
193 | udpClient.Shutdown(); | ||
194 | clients.Remove(client.AgentId, udpClient.RemoteEndPoint); | ||
195 | } | ||
196 | else | ||
197 | { | ||
198 | m_log.Warn("[LLUDPSERVER]: Failed to remove LLUDPClient for " + client.Name); | ||
199 | } | ||
200 | } | ||
201 | |||
202 | public void BroadcastPacket(Packet packet, ThrottleOutPacketType category, bool sendToPausedAgents, bool allowSplitting) | 195 | public void BroadcastPacket(Packet packet, ThrottleOutPacketType category, bool sendToPausedAgents, bool allowSplitting) |
203 | { | 196 | { |
204 | // CoarseLocationUpdate packets cannot be split in an automated way | 197 | // CoarseLocationUpdate packets cannot be split in an automated way |
@@ -216,30 +209,29 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
216 | for (int i = 0; i < packetCount; i++) | 209 | for (int i = 0; i < packetCount; i++) |
217 | { | 210 | { |
218 | byte[] data = datas[i]; | 211 | byte[] data = datas[i]; |
219 | clients.ForEach( | 212 | m_scene.ClientManager.ForEach( |
220 | delegate(LLUDPClient client) | 213 | delegate(IClientAPI client) |
221 | { SendPacketData(client, data, data.Length, packet.Type, packet.Header.Zerocoded, category); }); | 214 | { |
215 | if (client is LLClientView) | ||
216 | SendPacketData(((LLClientView)client).UDPClient, data, packet.Type, category); | ||
217 | } | ||
218 | ); | ||
222 | } | 219 | } |
223 | } | 220 | } |
224 | else | 221 | else |
225 | { | 222 | { |
226 | byte[] data = packet.ToBytes(); | 223 | byte[] data = packet.ToBytes(); |
227 | clients.ForEach( | 224 | m_scene.ClientManager.ForEach( |
228 | delegate(LLUDPClient client) | 225 | delegate(IClientAPI client) |
229 | { SendPacketData(client, data, data.Length, packet.Type, packet.Header.Zerocoded, category); }); | 226 | { |
227 | if (client is LLClientView) | ||
228 | SendPacketData(((LLClientView)client).UDPClient, data, packet.Type, category); | ||
229 | } | ||
230 | ); | ||
230 | } | 231 | } |
231 | } | 232 | } |
232 | 233 | ||
233 | public void SendPacket(UUID agentID, Packet packet, ThrottleOutPacketType category, bool allowSplitting) | 234 | public void SendPacket(LLUDPClient udpClient, Packet packet, ThrottleOutPacketType category, bool allowSplitting) |
234 | { | ||
235 | LLUDPClient client; | ||
236 | if (clients.TryGetValue(agentID, out client)) | ||
237 | SendPacket(client, packet, category, allowSplitting); | ||
238 | else | ||
239 | m_log.Warn("[LLUDPSERVER]: Attempted to send a packet to unknown agentID " + agentID); | ||
240 | } | ||
241 | |||
242 | public void SendPacket(LLUDPClient client, Packet packet, ThrottleOutPacketType category, bool allowSplitting) | ||
243 | { | 235 | { |
244 | // CoarseLocationUpdate packets cannot be split in an automated way | 236 | // CoarseLocationUpdate packets cannot be split in an automated way |
245 | if (packet.Type == PacketType.CoarseLocationUpdate && allowSplitting) | 237 | if (packet.Type == PacketType.CoarseLocationUpdate && allowSplitting) |
@@ -256,25 +248,28 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
256 | for (int i = 0; i < packetCount; i++) | 248 | for (int i = 0; i < packetCount; i++) |
257 | { | 249 | { |
258 | byte[] data = datas[i]; | 250 | byte[] data = datas[i]; |
259 | SendPacketData(client, data, data.Length, packet.Type, packet.Header.Zerocoded, category); | 251 | SendPacketData(udpClient, data, packet.Type, category); |
260 | } | 252 | } |
261 | } | 253 | } |
262 | else | 254 | else |
263 | { | 255 | { |
264 | byte[] data = packet.ToBytes(); | 256 | byte[] data = packet.ToBytes(); |
265 | SendPacketData(client, data, data.Length, packet.Type, packet.Header.Zerocoded, category); | 257 | SendPacketData(udpClient, data, packet.Type, category); |
266 | } | 258 | } |
267 | } | 259 | } |
268 | 260 | ||
269 | public void SendPacketData(LLUDPClient client, byte[] data, int dataLength, PacketType type, bool doZerocode, ThrottleOutPacketType category) | 261 | public void SendPacketData(LLUDPClient udpClient, byte[] data, PacketType type, ThrottleOutPacketType category) |
270 | { | 262 | { |
263 | int dataLength = data.Length; | ||
264 | bool doZerocode = (data[0] & Helpers.MSG_ZEROCODED) != 0; | ||
265 | |||
271 | // Frequency analysis of outgoing packet sizes shows a large clump of packets at each end of the spectrum. | 266 | // 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 | 267 | // 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 | 268 | // 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 | 269 | // to accomodate for both common scenarios and provide ample room for ACK appending in both |
275 | int bufferSize = (dataLength > 180) ? Packet.MTU : 200; | 270 | int bufferSize = (dataLength > 180) ? Packet.MTU : 200; |
276 | 271 | ||
277 | UDPPacketBuffer buffer = new UDPPacketBuffer(client.RemoteEndPoint, bufferSize); | 272 | UDPPacketBuffer buffer = new UDPPacketBuffer(udpClient.RemoteEndPoint, bufferSize); |
278 | 273 | ||
279 | // Zerocode if needed | 274 | // Zerocode if needed |
280 | if (doZerocode) | 275 | if (doZerocode) |
@@ -285,17 +280,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
285 | // The packet grew larger than the bufferSize while zerocoding. | 280 | // The packet grew larger than the bufferSize while zerocoding. |
286 | // Remove the MSG_ZEROCODED flag and send the unencoded data | 281 | // Remove the MSG_ZEROCODED flag and send the unencoded data |
287 | // instead | 282 | // instead |
288 | m_log.Info("[LLUDPSERVER]: Packet exceeded buffer size during zerocoding. Removing MSG_ZEROCODED flag"); | 283 | m_log.Debug("[LLUDPSERVER]: Packet exceeded buffer size during zerocoding for " + type + ". Removing MSG_ZEROCODED flag"); |
289 | data[0] = (byte)(data[0] & ~Helpers.MSG_ZEROCODED); | 284 | data[0] = (byte)(data[0] & ~Helpers.MSG_ZEROCODED); |
290 | // | ||
291 | buffer = new UDPPacketBuffer(client.RemoteEndPoint, dataLength); | ||
292 | // | ||
293 | Buffer.BlockCopy(data, 0, buffer.Data, 0, dataLength); | 285 | Buffer.BlockCopy(data, 0, buffer.Data, 0, dataLength); |
294 | } | 286 | } |
295 | } | 287 | } |
296 | else | 288 | else |
297 | { | 289 | { |
298 | // ??? will it fit? | ||
299 | Buffer.BlockCopy(data, 0, buffer.Data, 0, dataLength); | 290 | Buffer.BlockCopy(data, 0, buffer.Data, 0, dataLength); |
300 | } | 291 | } |
301 | buffer.DataLength = dataLength; | 292 | buffer.DataLength = dataLength; |
@@ -303,7 +294,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
303 | #region Queue or Send | 294 | #region Queue or Send |
304 | 295 | ||
305 | // Look up the UDPClient this is going to | 296 | // Look up the UDPClient this is going to |
306 | OutgoingPacket outgoingPacket = new OutgoingPacket(client, buffer, category); | 297 | OutgoingPacket outgoingPacket = new OutgoingPacket(udpClient, buffer, category); |
307 | 298 | ||
308 | if (!outgoingPacket.Client.EnqueueOutgoing(outgoingPacket)) | 299 | if (!outgoingPacket.Client.EnqueueOutgoing(outgoingPacket)) |
309 | SendPacketFinal(outgoingPacket); | 300 | SendPacketFinal(outgoingPacket); |
@@ -311,18 +302,18 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
311 | #endregion Queue or Send | 302 | #endregion Queue or Send |
312 | } | 303 | } |
313 | 304 | ||
314 | public void SendAcks(LLUDPClient client) | 305 | public void SendAcks(LLUDPClient udpClient) |
315 | { | 306 | { |
316 | uint ack; | 307 | uint ack; |
317 | 308 | ||
318 | if (client.PendingAcks.Dequeue(out ack)) | 309 | if (udpClient.PendingAcks.Dequeue(out ack)) |
319 | { | 310 | { |
320 | List<PacketAckPacket.PacketsBlock> blocks = new List<PacketAckPacket.PacketsBlock>(); | 311 | List<PacketAckPacket.PacketsBlock> blocks = new List<PacketAckPacket.PacketsBlock>(); |
321 | PacketAckPacket.PacketsBlock block = new PacketAckPacket.PacketsBlock(); | 312 | PacketAckPacket.PacketsBlock block = new PacketAckPacket.PacketsBlock(); |
322 | block.ID = ack; | 313 | block.ID = ack; |
323 | blocks.Add(block); | 314 | blocks.Add(block); |
324 | 315 | ||
325 | while (client.PendingAcks.Dequeue(out ack)) | 316 | while (udpClient.PendingAcks.Dequeue(out ack)) |
326 | { | 317 | { |
327 | block = new PacketAckPacket.PacketsBlock(); | 318 | block = new PacketAckPacket.PacketsBlock(); |
328 | block.ID = ack; | 319 | block.ID = ack; |
@@ -333,22 +324,39 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
333 | packet.Header.Reliable = false; | 324 | packet.Header.Reliable = false; |
334 | packet.Packets = blocks.ToArray(); | 325 | packet.Packets = blocks.ToArray(); |
335 | 326 | ||
336 | SendPacket(client, packet, ThrottleOutPacketType.Unknown, true); | 327 | SendPacket(udpClient, packet, ThrottleOutPacketType.Unknown, true); |
337 | } | 328 | } |
338 | } | 329 | } |
339 | 330 | ||
340 | public void SendPing(LLUDPClient client) | 331 | public void SendPing(LLUDPClient udpClient) |
341 | { | 332 | { |
342 | IClientAPI api = client.ClientAPI; | 333 | StartPingCheckPacket pc = (StartPingCheckPacket)PacketPool.Instance.GetPacket(PacketType.StartPingCheck); |
343 | if (api != null) | 334 | pc.Header.Reliable = false; |
344 | api.SendStartPingCheck(client.CurrentPingSequence++); | 335 | |
336 | OutgoingPacket oldestPacket = udpClient.NeedAcks.GetOldest(); | ||
337 | |||
338 | pc.PingID.PingID = (byte)udpClient.CurrentPingSequence++; | ||
339 | pc.PingID.OldestUnacked = (oldestPacket != null) ? oldestPacket.SequenceNumber : 0; | ||
340 | |||
341 | SendPacket(udpClient, pc, ThrottleOutPacketType.Unknown, false); | ||
345 | } | 342 | } |
346 | 343 | ||
347 | public void ResendUnacked(LLUDPClient client) | 344 | public void ResendUnacked(LLUDPClient udpClient) |
348 | { | 345 | { |
349 | if (client.NeedAcks.Count > 0) | 346 | if (udpClient.IsConnected && udpClient.NeedAcks.Count > 0) |
350 | { | 347 | { |
351 | List<OutgoingPacket> expiredPackets = client.NeedAcks.GetExpiredPackets(client.RTO); | 348 | // Disconnect an agent if no packets are received for some time |
349 | //FIXME: Make 60 an .ini setting | ||
350 | if (Environment.TickCount - udpClient.TickLastPacketReceived > 1000 * 60) | ||
351 | { | ||
352 | m_log.Warn("[LLUDPSERVER]: Ack timeout, disconnecting " + udpClient.AgentID); | ||
353 | |||
354 | RemoveClient(udpClient); | ||
355 | return; | ||
356 | } | ||
357 | |||
358 | // Get a list of all of the packets that have been sitting unacked longer than udpClient.RTO | ||
359 | List<OutgoingPacket> expiredPackets = udpClient.NeedAcks.GetExpiredPackets(udpClient.RTO); | ||
352 | 360 | ||
353 | if (expiredPackets != null) | 361 | if (expiredPackets != null) |
354 | { | 362 | { |
@@ -357,54 +365,30 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
357 | { | 365 | { |
358 | OutgoingPacket outgoingPacket = expiredPackets[i]; | 366 | OutgoingPacket outgoingPacket = expiredPackets[i]; |
359 | 367 | ||
360 | // FIXME: Make this an .ini setting | 368 | //m_log.DebugFormat("[LLUDPSERVER]: Resending packet #{0} (attempt {1}), {2}ms have passed", |
361 | if (outgoingPacket.ResendCount < 3) | 369 | // outgoingPacket.SequenceNumber, outgoingPacket.ResendCount, Environment.TickCount - outgoingPacket.TickCount); |
362 | { | ||
363 | //Logger.Debug(String.Format("Resending packet #{0} (attempt {1}), {2}ms have passed", | ||
364 | // outgoingPacket.SequenceNumber, outgoingPacket.ResendCount, Environment.TickCount - outgoingPacket.TickCount)); | ||
365 | |||
366 | // Set the resent flag | ||
367 | outgoingPacket.Buffer.Data[0] = (byte)(outgoingPacket.Buffer.Data[0] | Helpers.MSG_RESENT); | ||
368 | outgoingPacket.Category = ThrottleOutPacketType.Resend; | ||
369 | 370 | ||
370 | // The TickCount will be set to the current time when the packet | 371 | // Set the resent flag |
371 | // is actually sent out again | 372 | outgoingPacket.Buffer.Data[0] = (byte)(outgoingPacket.Buffer.Data[0] | Helpers.MSG_RESENT); |
372 | outgoingPacket.TickCount = 0; | 373 | outgoingPacket.Category = ThrottleOutPacketType.Resend; |
373 | 374 | ||
374 | // Bump up the resend count on this packet | 375 | // The TickCount will be set to the current time when the packet |
375 | Interlocked.Increment(ref outgoingPacket.ResendCount); | 376 | // is actually sent out again |
376 | //Interlocked.Increment(ref Stats.ResentPackets); | 377 | outgoingPacket.TickCount = 0; |
377 | 378 | ||
378 | // Queue or (re)send the packet | 379 | // Bump up the resend count on this packet |
379 | if (!outgoingPacket.Client.EnqueueOutgoing(outgoingPacket)) | 380 | Interlocked.Increment(ref outgoingPacket.ResendCount); |
380 | SendPacketFinal(outgoingPacket); | 381 | //Interlocked.Increment(ref Stats.ResentPackets); |
381 | } | ||
382 | else | ||
383 | { | ||
384 | m_log.DebugFormat("[LLUDPSERVER]: Dropping packet #{0} for agent {1} after {2} failed attempts", | ||
385 | outgoingPacket.SequenceNumber, outgoingPacket.Client.RemoteEndPoint, outgoingPacket.ResendCount); | ||
386 | 382 | ||
387 | lock (client.NeedAcks.SyncRoot) | 383 | // Requeue or resend the packet |
388 | client.NeedAcks.RemoveUnsafe(outgoingPacket.SequenceNumber); | 384 | if (!outgoingPacket.Client.EnqueueOutgoing(outgoingPacket)) |
389 | 385 | SendPacketFinal(outgoingPacket); | |
390 | //Interlocked.Increment(ref Stats.DroppedPackets); | ||
391 | |||
392 | // Disconnect an agent if no packets are received for some time | ||
393 | //FIXME: Make 60 an .ini setting | ||
394 | if (Environment.TickCount - client.TickLastPacketReceived > 1000 * 60) | ||
395 | { | ||
396 | m_log.Warn("[LLUDPSERVER]: Ack timeout, disconnecting " + client.ClientAPI.Name); | ||
397 | |||
398 | RemoveClient(client.ClientAPI); | ||
399 | return; | ||
400 | } | ||
401 | } | ||
402 | } | 386 | } |
403 | } | 387 | } |
404 | } | 388 | } |
405 | } | 389 | } |
406 | 390 | ||
407 | public void Flush() | 391 | public void Flush(LLUDPClient udpClient) |
408 | { | 392 | { |
409 | // FIXME: Implement? | 393 | // FIXME: Implement? |
410 | } | 394 | } |
@@ -415,7 +399,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
415 | byte flags = buffer.Data[0]; | 399 | byte flags = buffer.Data[0]; |
416 | bool isResend = (flags & Helpers.MSG_RESENT) != 0; | 400 | bool isResend = (flags & Helpers.MSG_RESENT) != 0; |
417 | bool isReliable = (flags & Helpers.MSG_RELIABLE) != 0; | 401 | bool isReliable = (flags & Helpers.MSG_RELIABLE) != 0; |
418 | LLUDPClient client = outgoingPacket.Client; | 402 | LLUDPClient udpClient = outgoingPacket.Client; |
403 | |||
404 | if (!udpClient.IsConnected) | ||
405 | return; | ||
419 | 406 | ||
420 | // Keep track of when this packet was sent out (right now) | 407 | // Keep track of when this packet was sent out (right now) |
421 | outgoingPacket.TickCount = Environment.TickCount; | 408 | outgoingPacket.TickCount = Environment.TickCount; |
@@ -424,11 +411,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
424 | 411 | ||
425 | int dataLength = buffer.DataLength; | 412 | int dataLength = buffer.DataLength; |
426 | 413 | ||
427 | // Keep appending ACKs until there is no room left in the packet or there are | 414 | // Keep appending ACKs until there is no room left in the buffer or there are |
428 | // no more ACKs to append | 415 | // no more ACKs to append |
429 | uint ackCount = 0; | 416 | uint ackCount = 0; |
430 | uint ack; | 417 | uint ack; |
431 | while (dataLength + 5 < buffer.Data.Length && client.PendingAcks.Dequeue(out ack)) | 418 | while (dataLength + 5 < buffer.Data.Length && udpClient.PendingAcks.Dequeue(out ack)) |
432 | { | 419 | { |
433 | Utils.UIntToBytesBig(ack, buffer.Data, dataLength); | 420 | Utils.UIntToBytesBig(ack, buffer.Data, dataLength); |
434 | dataLength += 4; | 421 | dataLength += 4; |
@@ -447,24 +434,28 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
447 | 434 | ||
448 | #endregion ACK Appending | 435 | #endregion ACK Appending |
449 | 436 | ||
437 | #region Sequence Number Assignment | ||
438 | |||
450 | if (!isResend) | 439 | if (!isResend) |
451 | { | 440 | { |
452 | // Not a resend, assign a new sequence number | 441 | // Not a resend, assign a new sequence number |
453 | uint sequenceNumber = (uint)Interlocked.Increment(ref client.CurrentSequence); | 442 | uint sequenceNumber = (uint)Interlocked.Increment(ref udpClient.CurrentSequence); |
454 | Utils.UIntToBytesBig(sequenceNumber, buffer.Data, 1); | 443 | Utils.UIntToBytesBig(sequenceNumber, buffer.Data, 1); |
455 | outgoingPacket.SequenceNumber = sequenceNumber; | 444 | outgoingPacket.SequenceNumber = sequenceNumber; |
456 | 445 | ||
457 | if (isReliable) | 446 | if (isReliable) |
458 | { | 447 | { |
459 | // Add this packet to the list of ACK responses we are waiting on from the server | 448 | // Add this packet to the list of ACK responses we are waiting on from the server |
460 | client.NeedAcks.Add(outgoingPacket); | 449 | udpClient.NeedAcks.Add(outgoingPacket); |
461 | } | 450 | } |
462 | } | 451 | } |
463 | 452 | ||
453 | #endregion Sequence Number Assignment | ||
454 | |||
464 | // Stats tracking | 455 | // Stats tracking |
465 | Interlocked.Increment(ref client.PacketsSent); | 456 | Interlocked.Increment(ref udpClient.PacketsSent); |
466 | if (isReliable) | 457 | if (isReliable) |
467 | Interlocked.Add(ref client.UnackedBytes, outgoingPacket.Buffer.DataLength); | 458 | Interlocked.Add(ref udpClient.UnackedBytes, outgoingPacket.Buffer.DataLength); |
468 | 459 | ||
469 | // Put the UDP payload on the wire | 460 | // Put the UDP payload on the wire |
470 | AsyncBeginSend(buffer); | 461 | AsyncBeginSend(buffer); |
@@ -473,10 +464,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
473 | protected override void PacketReceived(UDPPacketBuffer buffer) | 464 | protected override void PacketReceived(UDPPacketBuffer buffer) |
474 | { | 465 | { |
475 | // Debugging/Profiling | 466 | // Debugging/Profiling |
476 | //try { Thread.CurrentThread.Name = "PacketReceived (" + scene.RegionName + ")"; } | 467 | //try { Thread.CurrentThread.Name = "PacketReceived (" + m_scene.RegionInfo.RegionName + ")"; } |
477 | //catch (Exception) { } | 468 | //catch (Exception) { } |
478 | 469 | ||
479 | LLUDPClient client = null; | 470 | LLUDPClient udpClient = null; |
480 | Packet packet = null; | 471 | Packet packet = null; |
481 | int packetEnd = buffer.DataLength - 1; | 472 | int packetEnd = buffer.DataLength - 1; |
482 | IPEndPoint address = (IPEndPoint)buffer.RemoteEndPoint; | 473 | IPEndPoint address = (IPEndPoint)buffer.RemoteEndPoint; |
@@ -491,61 +482,59 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
491 | } | 482 | } |
492 | catch (MalformedDataException) | 483 | catch (MalformedDataException) |
493 | { | 484 | { |
494 | m_log.ErrorFormat("[LLUDPSERVER]: Malformed data, cannot parse packet:\n{0}", | 485 | m_log.ErrorFormat("[LLUDPSERVER]: Malformed data, cannot parse packet from {0}:\n{1}", |
495 | Utils.BytesToHexString(buffer.Data, buffer.DataLength, null)); | 486 | buffer.RemoteEndPoint, Utils.BytesToHexString(buffer.Data, buffer.DataLength, null)); |
496 | } | 487 | } |
497 | 488 | ||
498 | // Fail-safe check | 489 | // Fail-safe check |
499 | if (packet == null) | 490 | if (packet == null) |
500 | { | 491 | { |
501 | m_log.Warn("[LLUDPSERVER]: Couldn't build a message from the incoming data"); | 492 | m_log.Warn("[LLUDPSERVER]: Couldn't build a message from incoming data " + buffer.DataLength + |
493 | " bytes long from " + buffer.RemoteEndPoint); | ||
502 | return; | 494 | return; |
503 | } | 495 | } |
504 | 496 | ||
505 | //Stats.RecvBytes += (ulong)buffer.DataLength; | ||
506 | //++Stats.RecvPackets; | ||
507 | |||
508 | #endregion Decoding | 497 | #endregion Decoding |
509 | 498 | ||
510 | #region UseCircuitCode Handling | 499 | #region Packet to Client Mapping |
511 | 500 | ||
501 | // UseCircuitCode handling | ||
512 | if (packet.Type == PacketType.UseCircuitCode) | 502 | if (packet.Type == PacketType.UseCircuitCode) |
513 | { | 503 | { |
514 | UseCircuitCodePacket useCircuitCode = (UseCircuitCodePacket)packet; | 504 | AddNewClient((UseCircuitCodePacket)packet, (IPEndPoint)buffer.RemoteEndPoint); |
515 | IClientAPI newuser; | ||
516 | uint circuitCode = useCircuitCode.CircuitCode.Code; | ||
517 | |||
518 | // Check if the client is already established | ||
519 | if (!m_scene.ClientManager.TryGetClient(circuitCode, out newuser)) | ||
520 | { | ||
521 | AddNewClient(useCircuitCode, (IPEndPoint)buffer.RemoteEndPoint); | ||
522 | } | ||
523 | } | 505 | } |
524 | 506 | ||
525 | // Determine which agent this packet came from | 507 | // Determine which agent this packet came from |
526 | if (!clients.TryGetValue(address, out client)) | 508 | IClientAPI client; |
509 | if (!m_scene.ClientManager.TryGetValue(address, out client) || !(client is LLClientView)) | ||
527 | { | 510 | { |
528 | m_log.Warn("[LLUDPSERVER]: Received a " + packet.Type + " packet from an unrecognized source: " + address + " in " + m_scene.RegionInfo.RegionName); | 511 | m_log.Warn("[LLUDPSERVER]: Received a " + packet.Type + " packet from an unrecognized source: " + address + |
512 | " in " + m_scene.RegionInfo.RegionName + ", currently tracking " + m_scene.ClientManager.Count + " clients"); | ||
529 | return; | 513 | return; |
530 | } | 514 | } |
531 | 515 | ||
532 | #endregion UseCircuitCode Handling | 516 | udpClient = ((LLClientView)client).UDPClient; |
517 | |||
518 | if (!udpClient.IsConnected) | ||
519 | return; | ||
520 | |||
521 | #endregion Packet to Client Mapping | ||
533 | 522 | ||
534 | // Stats tracking | 523 | // Stats tracking |
535 | Interlocked.Increment(ref client.PacketsReceived); | 524 | Interlocked.Increment(ref udpClient.PacketsReceived); |
536 | 525 | ||
537 | #region ACK Receiving | 526 | #region ACK Receiving |
538 | 527 | ||
539 | int now = Environment.TickCount; | 528 | int now = Environment.TickCount; |
540 | client.TickLastPacketReceived = now; | 529 | udpClient.TickLastPacketReceived = now; |
541 | 530 | ||
542 | // Handle appended ACKs | 531 | // Handle appended ACKs |
543 | if (packet.Header.AppendedAcks && packet.Header.AckList != null) | 532 | if (packet.Header.AppendedAcks && packet.Header.AckList != null) |
544 | { | 533 | { |
545 | lock (client.NeedAcks.SyncRoot) | 534 | lock (udpClient.NeedAcks.SyncRoot) |
546 | { | 535 | { |
547 | for (int i = 0; i < packet.Header.AckList.Length; i++) | 536 | for (int i = 0; i < packet.Header.AckList.Length; i++) |
548 | AcknowledgePacket(client, packet.Header.AckList[i], now, packet.Header.Resent); | 537 | AcknowledgePacket(udpClient, packet.Header.AckList[i], now, packet.Header.Resent); |
549 | } | 538 | } |
550 | } | 539 | } |
551 | 540 | ||
@@ -554,10 +543,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
554 | { | 543 | { |
555 | PacketAckPacket ackPacket = (PacketAckPacket)packet; | 544 | PacketAckPacket ackPacket = (PacketAckPacket)packet; |
556 | 545 | ||
557 | lock (client.NeedAcks.SyncRoot) | 546 | lock (udpClient.NeedAcks.SyncRoot) |
558 | { | 547 | { |
559 | for (int i = 0; i < ackPacket.Packets.Length; i++) | 548 | for (int i = 0; i < ackPacket.Packets.Length; i++) |
560 | AcknowledgePacket(client, ackPacket.Packets[i].ID, now, packet.Header.Resent); | 549 | AcknowledgePacket(udpClient, ackPacket.Packets[i].ID, now, packet.Header.Resent); |
561 | } | 550 | } |
562 | } | 551 | } |
563 | 552 | ||
@@ -566,27 +555,27 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
566 | #region ACK Sending | 555 | #region ACK Sending |
567 | 556 | ||
568 | if (packet.Header.Reliable) | 557 | if (packet.Header.Reliable) |
569 | client.PendingAcks.Enqueue((uint)packet.Header.Sequence); | 558 | udpClient.PendingAcks.Enqueue(packet.Header.Sequence); |
570 | 559 | ||
571 | // This is a somewhat odd sequence of steps to pull the client.BytesSinceLastACK value out, | 560 | // This is a somewhat odd sequence of steps to pull the client.BytesSinceLastACK value out, |
572 | // add the current received bytes to it, test if 2*MTU bytes have been sent, if so remove | 561 | // add the current received bytes to it, test if 2*MTU bytes have been sent, if so remove |
573 | // 2*MTU bytes from the value and send ACKs, and finally add the local value back to | 562 | // 2*MTU bytes from the value and send ACKs, and finally add the local value back to |
574 | // client.BytesSinceLastACK. Lockless thread safety | 563 | // client.BytesSinceLastACK. Lockless thread safety |
575 | int bytesSinceLastACK = Interlocked.Exchange(ref client.BytesSinceLastACK, 0); | 564 | int bytesSinceLastACK = Interlocked.Exchange(ref udpClient.BytesSinceLastACK, 0); |
576 | bytesSinceLastACK += buffer.DataLength; | 565 | bytesSinceLastACK += buffer.DataLength; |
577 | if (bytesSinceLastACK > Packet.MTU * 2) | 566 | if (bytesSinceLastACK > Packet.MTU * 2) |
578 | { | 567 | { |
579 | bytesSinceLastACK -= Packet.MTU * 2; | 568 | bytesSinceLastACK -= Packet.MTU * 2; |
580 | SendAcks(client); | 569 | SendAcks(udpClient); |
581 | } | 570 | } |
582 | Interlocked.Add(ref client.BytesSinceLastACK, bytesSinceLastACK); | 571 | Interlocked.Add(ref udpClient.BytesSinceLastACK, bytesSinceLastACK); |
583 | 572 | ||
584 | #endregion ACK Sending | 573 | #endregion ACK Sending |
585 | 574 | ||
586 | #region Incoming Packet Accounting | 575 | #region Incoming Packet Accounting |
587 | 576 | ||
588 | // Check the archive of received reliable packet IDs to see whether we already received this packet | 577 | // Check the archive of received reliable packet IDs to see whether we already received this packet |
589 | if (packet.Header.Reliable && !client.PacketArchive.TryEnqueue(packet.Header.Sequence)) | 578 | if (packet.Header.Reliable && !udpClient.PacketArchive.TryEnqueue(packet.Header.Sequence)) |
590 | { | 579 | { |
591 | if (packet.Header.Resent) | 580 | if (packet.Header.Resent) |
592 | m_log.Debug("[LLUDPSERVER]: Received a resend of already processed packet #" + packet.Header.Sequence + ", type: " + packet.Type); | 581 | m_log.Debug("[LLUDPSERVER]: Received a resend of already processed packet #" + packet.Header.Sequence + ", type: " + packet.Type); |
@@ -603,7 +592,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
603 | if (packet.Type != PacketType.PacketAck) | 592 | if (packet.Type != PacketType.PacketAck) |
604 | { | 593 | { |
605 | // Inbox insertion | 594 | // Inbox insertion |
606 | packetInbox.Enqueue(new IncomingPacket(client, packet)); | 595 | packetInbox.Enqueue(new IncomingPacket(udpClient, packet)); |
607 | } | 596 | } |
608 | } | 597 | } |
609 | 598 | ||
@@ -623,53 +612,59 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
623 | 612 | ||
624 | private void AddNewClient(UseCircuitCodePacket useCircuitCode, IPEndPoint remoteEndPoint) | 613 | private void AddNewClient(UseCircuitCodePacket useCircuitCode, IPEndPoint remoteEndPoint) |
625 | { | 614 | { |
626 | //Slave regions don't accept new clients | 615 | UUID agentID = useCircuitCode.CircuitCode.ID; |
616 | UUID sessionID = useCircuitCode.CircuitCode.SessionID; | ||
617 | uint circuitCode = useCircuitCode.CircuitCode.Code; | ||
618 | |||
627 | if (m_scene.RegionStatus != RegionStatus.SlaveScene) | 619 | if (m_scene.RegionStatus != RegionStatus.SlaveScene) |
628 | { | 620 | { |
629 | AuthenticateResponse sessionInfo; | 621 | AuthenticateResponse sessionInfo; |
630 | bool isNewCircuit = !clients.ContainsKey(remoteEndPoint); | 622 | if (IsClientAuthorized(useCircuitCode, out sessionInfo)) |
631 | |||
632 | if (!IsClientAuthorized(useCircuitCode, out sessionInfo)) | ||
633 | { | 623 | { |
634 | m_log.WarnFormat( | 624 | AddClient(circuitCode, agentID, sessionID, remoteEndPoint, sessionInfo); |
635 | "[CONNECTION FAILURE]: Connection request for client {0} connecting with unnotified circuit code {1} from {2}", | ||
636 | useCircuitCode.CircuitCode.ID, useCircuitCode.CircuitCode.Code, remoteEndPoint); | ||
637 | return; | ||
638 | } | 625 | } |
639 | 626 | else | |
640 | if (isNewCircuit) | ||
641 | { | 627 | { |
642 | UUID agentID = useCircuitCode.CircuitCode.ID; | 628 | // Don't create circuits for unauthorized clients |
643 | UUID sessionID = useCircuitCode.CircuitCode.SessionID; | 629 | m_log.WarnFormat( |
644 | uint circuitCode = useCircuitCode.CircuitCode.Code; | 630 | "[LLUDPSERVER]: Connection request for client {0} connecting with unnotified circuit code {1} from {2}", |
645 | 631 | useCircuitCode.CircuitCode.ID, useCircuitCode.CircuitCode.Code, remoteEndPoint); | |
646 | AddClient(circuitCode, agentID, sessionID, remoteEndPoint, sessionInfo); | ||
647 | } | 632 | } |
648 | } | 633 | } |
634 | else | ||
635 | { | ||
636 | // Slave regions don't accept new clients | ||
637 | m_log.Debug("[LLUDPSERVER]: Slave region " + m_scene.RegionInfo.RegionName + " ignoring UseCircuitCode packet"); | ||
638 | } | ||
649 | } | 639 | } |
650 | 640 | ||
651 | private void AddClient(uint circuitCode, UUID agentID, UUID sessionID, IPEndPoint remoteEndPoint, AuthenticateResponse sessionInfo) | 641 | private void AddClient(uint circuitCode, UUID agentID, UUID sessionID, IPEndPoint remoteEndPoint, AuthenticateResponse sessionInfo) |
652 | { | 642 | { |
653 | // Create the LLUDPClient | 643 | // Create the LLUDPClient |
654 | LLUDPClient client = new LLUDPClient(this, m_throttleRates, m_throttle, circuitCode, agentID, remoteEndPoint); | 644 | LLUDPClient udpClient = new LLUDPClient(this, m_throttleRates, m_throttle, circuitCode, agentID, remoteEndPoint); |
655 | |||
656 | // Create the LLClientView | ||
657 | LLClientView clientApi = new LLClientView(remoteEndPoint, m_scene, this, client, sessionInfo, agentID, sessionID, circuitCode); | ||
658 | clientApi.OnViewerEffect += m_scene.ClientManager.ViewerEffectHandler; | ||
659 | clientApi.OnLogout += LogoutHandler; | ||
660 | clientApi.OnConnectionClosed += RemoveClient; | ||
661 | |||
662 | // Start the IClientAPI | ||
663 | m_scene.ClientManager.Add(circuitCode, clientApi); | ||
664 | clientApi.Start(); | ||
665 | 645 | ||
666 | // Give LLUDPClient a reference to IClientAPI | 646 | if (!m_scene.ClientManager.ContainsKey(agentID)) |
667 | client.ClientAPI = clientApi; | 647 | { |
648 | // Create the LLClientView | ||
649 | LLClientView client = new LLClientView(remoteEndPoint, m_scene, this, udpClient, sessionInfo, agentID, sessionID, circuitCode); | ||
650 | client.OnLogout += LogoutHandler; | ||
668 | 651 | ||
669 | // Add the new client to our list of tracked clients | 652 | // Start the IClientAPI |
670 | clients.Add(agentID, client.RemoteEndPoint, client); | 653 | client.Start(); |
654 | } | ||
655 | else | ||
656 | { | ||
657 | m_log.WarnFormat("[LLUDPSERVER]: Ignoring a repeated UseCircuitCode from {0} at {1} for circuit {2}", | ||
658 | udpClient.AgentID, remoteEndPoint, circuitCode); | ||
659 | } | ||
660 | } | ||
671 | 661 | ||
672 | m_log.DebugFormat("[LLUDPSERVER]: Added new client {0} to region {1}", agentID, m_scene.RegionInfo.RegionName); | 662 | private void RemoveClient(LLUDPClient udpClient) |
663 | { | ||
664 | // Remove this client from the scene | ||
665 | IClientAPI client; | ||
666 | if (m_scene.ClientManager.TryGetValue(udpClient.AgentID, out client)) | ||
667 | client.Close(); | ||
673 | } | 668 | } |
674 | 669 | ||
675 | private void AcknowledgePacket(LLUDPClient client, uint ack, int currentTime, bool fromResend) | 670 | private void AcknowledgePacket(LLUDPClient client, uint ack, int currentTime, bool fromResend) |
@@ -747,20 +742,28 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
747 | elapsed500MS = 0; | 742 | elapsed500MS = 0; |
748 | } | 743 | } |
749 | 744 | ||
750 | clients.ForEach( | 745 | m_scene.ClientManager.ForEach( |
751 | delegate(LLUDPClient client) | 746 | delegate(IClientAPI client) |
752 | { | 747 | { |
753 | if (client.DequeueOutgoing()) | 748 | if (client is LLClientView) |
754 | packetSent = true; | ||
755 | if (resendUnacked) | ||
756 | ResendUnacked(client); | ||
757 | if (sendAcks) | ||
758 | { | 749 | { |
759 | SendAcks(client); | 750 | LLUDPClient udpClient = ((LLClientView)client).UDPClient; |
760 | client.SendPacketStats(); | 751 | |
752 | if (udpClient.IsConnected) | ||
753 | { | ||
754 | if (udpClient.DequeueOutgoing()) | ||
755 | packetSent = true; | ||
756 | if (resendUnacked) | ||
757 | ResendUnacked(udpClient); | ||
758 | if (sendAcks) | ||
759 | { | ||
760 | SendAcks(udpClient); | ||
761 | udpClient.SendPacketStats(); | ||
762 | } | ||
763 | if (sendPings) | ||
764 | SendPing(udpClient); | ||
765 | } | ||
761 | } | 766 | } |
762 | if (sendPings) | ||
763 | SendPing(client); | ||
764 | } | 767 | } |
765 | ); | 768 | ); |
766 | 769 | ||
@@ -773,38 +776,48 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
773 | { | 776 | { |
774 | IncomingPacket incomingPacket = (IncomingPacket)state; | 777 | IncomingPacket incomingPacket = (IncomingPacket)state; |
775 | Packet packet = incomingPacket.Packet; | 778 | Packet packet = incomingPacket.Packet; |
776 | LLUDPClient client = incomingPacket.Client; | 779 | LLUDPClient udpClient = incomingPacket.Client; |
780 | IClientAPI client; | ||
777 | 781 | ||
778 | // Sanity check | 782 | // Sanity check |
779 | if (packet == null || client == null || client.ClientAPI == null) | 783 | if (packet == null || udpClient == null) |
780 | { | 784 | { |
781 | m_log.WarnFormat("[LLUDPSERVER]: Processing a packet with incomplete state. Packet=\"{0}\", Client=\"{1}\", Client.ClientAPI=\"{2}\"", | 785 | m_log.WarnFormat("[LLUDPSERVER]: Processing a packet with incomplete state. Packet=\"{0}\", UDPClient=\"{1}\"", |
782 | packet, client, (client != null) ? client.ClientAPI : null); | 786 | packet, udpClient); |
783 | } | 787 | } |
784 | 788 | ||
785 | try | 789 | // Make sure this client is still alive |
786 | { | 790 | if (m_scene.ClientManager.TryGetValue(udpClient.AgentID, out client)) |
787 | // Process this packet | ||
788 | client.ClientAPI.ProcessInPacket(packet); | ||
789 | } | ||
790 | catch (ThreadAbortException) | ||
791 | { | 791 | { |
792 | // If something is trying to abort the packet processing thread, take that as a hint that it's time to shut down | 792 | try |
793 | m_log.Info("[LLUDPSERVER]: Caught a thread abort, shutting down the LLUDP server"); | 793 | { |
794 | Stop(); | 794 | // Process this packet |
795 | client.ProcessInPacket(packet); | ||
796 | } | ||
797 | catch (ThreadAbortException) | ||
798 | { | ||
799 | // If something is trying to abort the packet processing thread, take that as a hint that it's time to shut down | ||
800 | m_log.Info("[LLUDPSERVER]: Caught a thread abort, shutting down the LLUDP server"); | ||
801 | Stop(); | ||
802 | } | ||
803 | catch (Exception e) | ||
804 | { | ||
805 | // Don't let a failure in an individual client thread crash the whole sim. | ||
806 | m_log.ErrorFormat("[LLUDPSERVER]: Client packet handler for {0} for packet {1} threw an exception", udpClient.AgentID, packet.Type); | ||
807 | m_log.Error(e.Message, e); | ||
808 | } | ||
795 | } | 809 | } |
796 | catch (Exception e) | 810 | else |
797 | { | 811 | { |
798 | // Don't let a failure in an individual client thread crash the whole sim. | 812 | m_log.DebugFormat("[LLUDPSERVER]: Dropping incoming {0} packet for dead client {1}", packet.Type, udpClient.AgentID); |
799 | m_log.ErrorFormat("[LLUDPSERVER]: Client packet handler for {0} for packet {1} threw an exception", client.AgentID, packet.Type); | ||
800 | m_log.Error(e.Message, e); | ||
801 | } | 813 | } |
802 | } | 814 | } |
803 | 815 | ||
804 | private void LogoutHandler(IClientAPI client) | 816 | private void LogoutHandler(IClientAPI client) |
805 | { | 817 | { |
806 | client.SendLogoutPacket(); | 818 | client.SendLogoutPacket(); |
807 | RemoveClient(client); | 819 | if (client.IsActive) |
820 | RemoveClient(((LLClientView)client).UDPClient); | ||
808 | } | 821 | } |
809 | } | 822 | } |
810 | } | 823 | } |