aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs')
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs1055
1 files changed, 609 insertions, 446 deletions
diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs
index c779b08..2c5ad85 100644
--- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs
+++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs
@@ -26,616 +26,779 @@
26 */ 26 */
27 27
28using System; 28using System;
29using System.Collections;
30using System.Collections.Generic; 29using System.Collections.Generic;
31using System.Net; 30using System.Net;
32using System.Net.Sockets; 31using System.Net.Sockets;
33using System.Reflection; 32using System.Reflection;
33using System.Threading;
34using log4net; 34using log4net;
35using Nini.Config; 35using Nini.Config;
36using OpenMetaverse.Packets; 36using OpenMetaverse.Packets;
37using OpenSim.Framework; 37using OpenSim.Framework;
38using OpenSim.Framework.Statistics;
38using OpenSim.Region.Framework.Scenes; 39using OpenSim.Region.Framework.Scenes;
40using OpenMetaverse;
39 41
40namespace OpenSim.Region.ClientStack.LindenUDP 42namespace OpenSim.Region.ClientStack.LindenUDP
41{ 43{
42 /// <summary> 44 /// <summary>
43 /// This class handles the initial UDP circuit setup with a client and passes on subsequent packets to the LLPacketServer 45 /// A shim around LLUDPServer that implements the IClientNetworkServer interface
44 /// </summary> 46 /// </summary>
45 public class LLUDPServer : ILLClientStackNetworkHandler, IClientNetworkServer 47 public sealed class LLUDPServerShim : IClientNetworkServer
46 { 48 {
47 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); 49 LLUDPServer m_udpServer;
48
49 /// <value>
50 /// The client circuits established with this UDP server. If a client exists here we can also assume that
51 /// it is populated in clientCircuits_reverse and proxyCircuits (if relevant)
52 /// </value>
53 protected Dictionary<EndPoint, uint> clientCircuits = new Dictionary<EndPoint, uint>();
54 public Hashtable clientCircuits_reverse = Hashtable.Synchronized(new Hashtable());
55 protected Dictionary<uint, EndPoint> proxyCircuits = new Dictionary<uint, EndPoint>();
56
57 private Socket m_socket;
58 protected IPEndPoint ServerIncoming;
59 protected byte[] RecvBuffer = new byte[4096];
60 protected byte[] ZeroBuffer = new byte[8192];
61 50
62 /// <value> 51 public LLUDPServerShim()
63 /// This is an endpoint that is reused where we don't need to protect the information from potentially 52 {
64 /// being stomped on by other threads. 53 }
65 /// </value>
66 protected EndPoint reusedEpSender = new IPEndPoint(IPAddress.Any, 0);
67
68 protected int proxyPortOffset;
69
70 protected AsyncCallback ReceivedData;
71 protected LLPacketServer m_packetServer;
72 protected Location m_location;
73 54
74 protected uint listenPort; 55 public void Initialise(IPAddress listenIP, ref uint port, int proxyPortOffsetParm, bool allow_alternate_port, IConfigSource configSource, AgentCircuitManager circuitManager)
75 protected bool Allow_Alternate_Port; 56 {
76 protected IPAddress listenIP = IPAddress.Parse("0.0.0.0"); 57 m_udpServer = new LLUDPServer(listenIP, ref port, proxyPortOffsetParm, allow_alternate_port, configSource, circuitManager);
77 protected IScene m_localScene; 58 }
78 protected int m_clientSocketReceiveBuffer = 0;
79 59
80 /// <value> 60 public void NetworkStop()
81 /// Manages authentication for agent circuits 61 {
82 /// </value> 62 m_udpServer.Stop();
83 protected AgentCircuitManager m_circuitManager; 63 }
84 64
85 public IScene LocalScene 65 public void AddScene(IScene scene)
86 { 66 {
87 set 67 m_udpServer.AddScene(scene);
88 { 68 }
89 m_localScene = value;
90 m_packetServer.LocalScene = m_localScene;
91 69
92 m_location = new Location(m_localScene.RegionInfo.RegionHandle); 70 public bool HandlesRegion(Location x)
93 } 71 {
72 return m_udpServer.HandlesRegion(x);
94 } 73 }
95 74
96 public ulong RegionHandle 75 public void Start()
97 { 76 {
98 get { return m_location.RegionHandle; } 77 m_udpServer.Start();
99 } 78 }
100 79
101 Socket IClientNetworkServer.Server 80 public void Stop()
102 { 81 {
103 get { return m_socket; } 82 m_udpServer.Stop();
104 } 83 }
84 }
105 85
106 public bool HandlesRegion(Location x) 86 /// <summary>
87 /// The LLUDP server for a region. This handles incoming and outgoing
88 /// packets for all UDP connections to the region
89 /// </summary>
90 public class LLUDPServer : UDPBase
91 {
92 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
93
94 /// <summary>Handlers for incoming packets</summary>
95 //PacketEventDictionary packetEvents = new PacketEventDictionary();
96 /// <summary>Incoming packets that are awaiting handling</summary>
97 private OpenMetaverse.BlockingQueue<IncomingPacket> packetInbox = new OpenMetaverse.BlockingQueue<IncomingPacket>();
98 /// <summary></summary>
99 private UDPClientCollection clients = new UDPClientCollection();
100 /// <summary>Bandwidth throttle for this UDP server</summary>
101 private TokenBucket m_throttle;
102 /// <summary>Bandwidth throttle rates for this UDP server</summary>
103 private ThrottleRates m_throttleRates;
104 /// <summary>Manages authentication for agent circuits</summary>
105 private AgentCircuitManager m_circuitManager;
106 /// <summary>Reference to the scene this UDP server is attached to</summary>
107 private IScene m_scene;
108 /// <summary>The X/Y coordinates of the scene this UDP server is attached to</summary>
109 private Location m_location;
110 /// <summary>The measured resolution of Environment.TickCount</summary>
111 private float m_tickCountResolution;
112
113 /// <summary>The measured resolution of Environment.TickCount</summary>
114 public float TickCountResolution { get { return m_tickCountResolution; } }
115 public Socket Server { get { return null; } }
116
117 public LLUDPServer(IPAddress listenIP, ref uint port, int proxyPortOffsetParm, bool allow_alternate_port, IConfigSource configSource, AgentCircuitManager circuitManager)
118 : base((int)port)
107 { 119 {
108 //return x.RegionHandle == m_location.RegionHandle; 120 #region Environment.TickCount Measurement
109 return x == m_location; 121
122 // Measure the resolution of Environment.TickCount
123 m_tickCountResolution = 0f;
124 for (int i = 0; i < 5; i++)
125 {
126 int start = Environment.TickCount;
127 int now = start;
128 while (now == start)
129 now = Environment.TickCount;
130 m_tickCountResolution += (float)(now - start) * 0.2f;
131 }
132 m_log.Info("[LLUDPSERVER]: Average Environment.TickCount resolution: " + TickCountResolution + "ms");
133
134 #endregion Environment.TickCount Measurement
135
136 m_circuitManager = circuitManager;
137
138 // TODO: Config support for throttling the entire connection
139 m_throttle = new TokenBucket(null, 0, 0);
140 m_throttleRates = new ThrottleRates(configSource);
110 } 141 }
111 142
112 public void AddScene(IScene x) 143 public new void Start()
113 { 144 {
114 LocalScene = x; 145 if (m_scene == null)
146 throw new InvalidOperationException("Cannot LLUDPServer.Start() without an IScene reference");
147
148 base.Start();
149
150 // Start the incoming packet processing thread
151 Thread incomingThread = new Thread(IncomingPacketHandler);
152 incomingThread.Name = "Incoming Packets (" + m_scene.RegionInfo.RegionName + ")";
153 incomingThread.Start();
154
155 Thread outgoingThread = new Thread(OutgoingPacketHandler);
156 outgoingThread.Name = "Outgoing Packets (" + m_scene.RegionInfo.RegionName + ")";
157 outgoingThread.Start();
115 } 158 }
116 159
117 public void Start() 160 public new void Stop()
118 { 161 {
119 ServerListener(); 162 m_log.Info("[LLUDPSERVER]: Shutting down the LLUDP server for " + m_scene.RegionInfo.RegionName);
163 base.Stop();
120 } 164 }
121 165
122 public void Stop() 166 public void AddScene(IScene scene)
123 { 167 {
124 m_socket.Close(); 168 if (m_scene == null)
169 {
170 m_scene = scene;
171 m_location = new Location(m_scene.RegionInfo.RegionHandle);
172 }
173 else
174 {
175 m_log.Error("[LLUDPSERVER]: AddScene() called on an LLUDPServer that already has a scene");
176 }
125 } 177 }
126 178
127 public LLUDPServer() 179 public bool HandlesRegion(Location x)
128 { 180 {
181 return x == m_location;
129 } 182 }
130 183
131 public LLUDPServer( 184 public void RemoveClient(IClientAPI client)
132 IPAddress _listenIP, ref uint port, int proxyPortOffset, bool allow_alternate_port, IConfigSource configSource,
133 AgentCircuitManager authenticateClass)
134 { 185 {
135 Initialise(_listenIP, ref port, proxyPortOffset, allow_alternate_port, configSource, authenticateClass); 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);
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 }
136 } 200 }
137 201
138 /// <summary> 202 public void BroadcastPacket(Packet packet, ThrottleOutPacketType category, bool sendToPausedAgents, bool allowSplitting)
139 /// Initialize the server
140 /// </summary>
141 /// <param name="_listenIP"></param>
142 /// <param name="port"></param>
143 /// <param name="proxyPortOffsetParm"></param>
144 /// <param name="allow_alternate_port"></param>
145 /// <param name="configSource"></param>
146 /// <param name="assetCache"></param>
147 /// <param name="circuitManager"></param>
148 public void Initialise(
149 IPAddress _listenIP, ref uint port, int proxyPortOffsetParm, bool allow_alternate_port, IConfigSource configSource,
150 AgentCircuitManager circuitManager)
151 { 203 {
152 ClientStackUserSettings userSettings = new ClientStackUserSettings(); 204 // CoarseLocationUpdate packets cannot be split in an automated way
153 205 if (packet.Type == PacketType.CoarseLocationUpdate && allowSplitting)
154 IConfig config = configSource.Configs["ClientStack.LindenUDP"]; 206 allowSplitting = false;
155 207
156 if (config != null) 208 if (allowSplitting && packet.HasVariableBlocks)
157 { 209 {
158 if (config.Contains("client_throttle_max_bps")) 210 byte[][] datas = packet.ToBytesMultiple();
159 { 211 int packetCount = datas.Length;
160 int maxBPS = config.GetInt("client_throttle_max_bps", 1500000);
161 userSettings.TotalThrottleSettings = new ThrottleSettings(0, maxBPS,
162 maxBPS > 28000 ? maxBPS : 28000);
163 }
164 212
165 if (config.Contains("client_throttle_multiplier")) 213 //if (packetCount > 1)
166 userSettings.ClientThrottleMultipler = config.GetFloat("client_throttle_multiplier"); 214 // m_log.Debug("[LLUDPSERVER]: Split " + packet.Type + " packet into " + packetCount + " packets");
167 if (config.Contains("client_socket_rcvbuf_size"))
168 m_clientSocketReceiveBuffer = config.GetInt("client_socket_rcvbuf_size");
169 }
170
171 m_log.DebugFormat("[CLIENT]: client_throttle_multiplier = {0}", userSettings.ClientThrottleMultipler);
172 m_log.DebugFormat("[CLIENT]: client_socket_rcvbuf_size = {0}", (m_clientSocketReceiveBuffer != 0 ?
173 m_clientSocketReceiveBuffer.ToString() : "OS default"));
174
175 proxyPortOffset = proxyPortOffsetParm;
176 listenPort = (uint) (port + proxyPortOffsetParm);
177 listenIP = _listenIP;
178 Allow_Alternate_Port = allow_alternate_port;
179 m_circuitManager = circuitManager;
180 CreatePacketServer(userSettings);
181 215
182 // Return new port 216 for (int i = 0; i < packetCount; i++)
183 // This because in Grid mode it is not really important what port the region listens to as long as it is correctly registered. 217 {
184 // So the option allow_alternate_ports="true" was added to default.xml 218 byte[] data = datas[i];
185 port = (uint)(listenPort - proxyPortOffsetParm); 219 clients.ForEach(
220 delegate(LLUDPClient client)
221 { SendPacketData(client, data, data.Length, packet.Type, packet.Header.Zerocoded, category); });
222 }
223 }
224 else
225 {
226 byte[] data = packet.ToBytes();
227 clients.ForEach(
228 delegate(LLUDPClient client)
229 { SendPacketData(client, data, data.Length, packet.Type, packet.Header.Zerocoded, category); });
230 }
186 } 231 }
187 232
188 protected virtual void CreatePacketServer(ClientStackUserSettings userSettings) 233 public void SendPacket(UUID agentID, Packet packet, ThrottleOutPacketType category, bool allowSplitting)
189 { 234 {
190 new LLPacketServer(this, userSettings); 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);
191 } 240 }
192 241
193 /// <summary> 242 public void SendPacket(LLUDPClient client, Packet packet, ThrottleOutPacketType category, bool allowSplitting)
194 /// This method is called every time that we receive new UDP data.
195 /// </summary>
196 /// <param name="result"></param>
197 protected virtual void OnReceivedData(IAsyncResult result)
198 { 243 {
199 Packet packet = null; 244 // CoarseLocationUpdate packets cannot be split in an automated way
200 int numBytes = 1; 245 if (packet.Type == PacketType.CoarseLocationUpdate && allowSplitting)
201 EndPoint epSender = new IPEndPoint(IPAddress.Any, 0); 246 allowSplitting = false;
202 EndPoint epProxy = null;
203 247
204 try 248 if (allowSplitting && packet.HasVariableBlocks)
205 { 249 {
206 if (EndReceive(out numBytes, result, ref epSender)) 250 byte[][] datas = packet.ToBytesMultiple();
207 { 251 int packetCount = datas.Length;
208 // Make sure we are getting zeroes when running off the 252
209 // end of grab / degrab packets from old clients 253 //if (packetCount > 1)
210 Array.Clear(RecvBuffer, numBytes, RecvBuffer.Length - numBytes); 254 // m_log.Debug("[LLUDPSERVER]: Split " + packet.Type + " packet into " + packetCount + " packets");
211 255
212 int packetEnd = numBytes - 1; 256 for (int i = 0; i < packetCount; i++)
213 if (proxyPortOffset != 0) packetEnd -= 6;
214
215 try
216 {
217 packet = PacketPool.Instance.GetPacket(RecvBuffer, ref packetEnd, ZeroBuffer);
218 }
219 catch (MalformedDataException e)
220 {
221 m_log.DebugFormat("[CLIENT]: Dropped Malformed Packet due to MalformedDataException: {0}", e.StackTrace);
222 }
223 catch (IndexOutOfRangeException e)
224 {
225 m_log.DebugFormat("[CLIENT]: Dropped Malformed Packet due to IndexOutOfRangeException: {0}", e.StackTrace);
226 }
227 catch (Exception e)
228 {
229 m_log.Debug("[CLIENT]: " + e);
230 }
231 }
232
233
234 if (proxyPortOffset != 0)
235 { 257 {
236 // If we've received a use circuit packet, then we need to decode an endpoint proxy, if one exists, 258 byte[] data = datas[i];
237 // before allowing the RecvBuffer to be overwritten by the next packet. 259 SendPacketData(client, data, data.Length, packet.Type, packet.Header.Zerocoded, category);
238 if (packet != null && packet.Type == PacketType.UseCircuitCode)
239 {
240 epProxy = epSender;
241 }
242
243 // Now decode the message from the proxy server
244 epSender = ProxyCodec.DecodeProxyMessage(RecvBuffer, ref numBytes);
245 } 260 }
246 } 261 }
247 catch (Exception ex) 262 else
248 { 263 {
249 m_log.ErrorFormat("[CLIENT]: Exception thrown during EndReceive(): {0}", ex); 264 byte[] data = packet.ToBytes();
265 SendPacketData(client, data, data.Length, packet.Type, packet.Header.Zerocoded, category);
250 } 266 }
267 }
251 268
252 BeginRobustReceive(); 269 public void SendPacketData(LLUDPClient client, byte[] data, int dataLength, PacketType type, bool doZerocode, ThrottleOutPacketType category)
270 {
271 // 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
273 // 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
275 int bufferSize = (dataLength > 180) ? Packet.MTU : 200;
276
277 UDPPacketBuffer buffer = new UDPPacketBuffer(client.RemoteEndPoint, bufferSize);
253 278
254 if (packet != null) 279 // Zerocode if needed
280 if (doZerocode)
255 { 281 {
256 if (packet.Type == PacketType.UseCircuitCode) 282 try { dataLength = Helpers.ZeroEncode(data, dataLength, buffer.Data); }
257 AddNewClient((UseCircuitCodePacket)packet, epSender, epProxy); 283 catch (IndexOutOfRangeException)
258 else 284 {
259 ProcessInPacket(packet, epSender); 285 // The packet grew larger than the bufferSize while zerocoding.
286 // Remove the MSG_ZEROCODED flag and send the unencoded data
287 // instead
288 m_log.Info("[LLUDPSERVER]: Packet exceeded buffer size during zerocoding. Removing MSG_ZEROCODED flag");
289 data[0] = (byte)(data[0] & ~Helpers.MSG_ZEROCODED);
290 Buffer.BlockCopy(data, 0, buffer.Data, 0, dataLength);
291 }
292 }
293 else
294 {
295 Buffer.BlockCopy(data, 0, buffer.Data, 0, dataLength);
260 } 296 }
297 buffer.DataLength = dataLength;
298
299 #region Queue or Send
300
301 // Look up the UDPClient this is going to
302 OutgoingPacket outgoingPacket = new OutgoingPacket(client, buffer, category);
303
304 if (!outgoingPacket.Client.EnqueueOutgoing(outgoingPacket))
305 SendPacketFinal(outgoingPacket);
306
307 #endregion Queue or Send
261 } 308 }
262 309
263 /// <summary> 310 public void SendAcks(LLUDPClient client)
264 /// Process a successfully received packet.
265 /// </summary>
266 /// <param name="packet"></param>
267 /// <param name="epSender"></param>
268 protected virtual void ProcessInPacket(Packet packet, EndPoint epSender)
269 { 311 {
270 try 312 uint ack;
313
314 if (client.PendingAcks.Dequeue(out ack))
271 { 315 {
272 // do we already have a circuit for this endpoint 316 List<PacketAckPacket.PacketsBlock> blocks = new List<PacketAckPacket.PacketsBlock>();
273 uint circuit; 317 PacketAckPacket.PacketsBlock block = new PacketAckPacket.PacketsBlock();
274 bool ret; 318 block.ID = ack;
275 319 blocks.Add(block);
276 lock (clientCircuits) 320
321 while (client.PendingAcks.Dequeue(out ack))
277 { 322 {
278 ret = clientCircuits.TryGetValue(epSender, out circuit); 323 block = new PacketAckPacket.PacketsBlock();
324 block.ID = ack;
325 blocks.Add(block);
279 } 326 }
280 327
281 if (ret) 328 PacketAckPacket packet = new PacketAckPacket();
282 { 329 packet.Header.Reliable = false;
283 //if so then send packet to the packetserver 330 packet.Packets = blocks.ToArray();
284 //m_log.DebugFormat(
285 // "[UDPSERVER]: For circuit {0} {1} got packet {2}", circuit, epSender, packet.Type);
286 331
287 m_packetServer.InPacket(circuit, packet); 332 SendPacket(client, packet, ThrottleOutPacketType.Unknown, true);
288 }
289 }
290 catch (Exception e)
291 {
292 m_log.Error("[CLIENT]: Exception in processing packet - ignoring: ", e);
293 } 333 }
294 } 334 }
295 335
296 /// <summary> 336 public void SendPing(LLUDPClient client)
297 /// Begin an asynchronous receive of the next bit of raw data
298 /// </summary>
299 protected virtual void BeginReceive()
300 { 337 {
301 m_socket.BeginReceiveFrom( 338 IClientAPI api = client.ClientAPI;
302 RecvBuffer, 0, RecvBuffer.Length, SocketFlags.None, ref reusedEpSender, ReceivedData, null); 339 if (api != null)
340 api.SendStartPingCheck(client.CurrentPingSequence++);
303 } 341 }
304 342
305 /// <summary> 343 public void ResendUnacked(LLUDPClient client)
306 /// Begin a robust asynchronous receive of the next bit of raw data. Robust means that SocketExceptions are
307 /// automatically dealt with until the next set of valid UDP data is received.
308 /// </summary>
309 private void BeginRobustReceive()
310 { 344 {
311 bool done = false; 345 if (client.NeedAcks.Count > 0)
312
313 while (!done)
314 { 346 {
315 try 347 List<OutgoingPacket> expiredPackets = client.NeedAcks.GetExpiredPackets(client.RTO);
316 {
317 BeginReceive();
318 done = true;
319 }
320 catch (SocketException e)
321 {
322 // ENDLESS LOOP ON PURPOSE!
323 // Reset connection and get next UDP packet off the buffer
324 // If the UDP packet is part of the same stream, this will happen several hundreds of times before
325 // the next set of UDP data is for a valid client.
326 348
327 try 349 if (expiredPackets != null)
328 { 350 {
329 CloseCircuit(e); 351 // Resend packets
330 } 352 for (int i = 0; i < expiredPackets.Count; i++)
331 catch (Exception e2)
332 { 353 {
333 m_log.ErrorFormat( 354 OutgoingPacket outgoingPacket = expiredPackets[i];
334 "[CLIENT]: Exception thrown when trying to close the circuit for {0} - {1}", reusedEpSender, 355
335 e2); 356 // FIXME: Make this an .ini setting
357 if (outgoingPacket.ResendCount < 3)
358 {
359 //Logger.Debug(String.Format("Resending packet #{0} (attempt {1}), {2}ms have passed",
360 // outgoingPacket.SequenceNumber, outgoingPacket.ResendCount, Environment.TickCount - outgoingPacket.TickCount));
361
362 // Set the resent flag
363 outgoingPacket.Buffer.Data[0] = (byte)(outgoingPacket.Buffer.Data[0] | Helpers.MSG_RESENT);
364 outgoingPacket.Category = ThrottleOutPacketType.Resend;
365
366 // The TickCount will be set to the current time when the packet
367 // is actually sent out again
368 outgoingPacket.TickCount = 0;
369
370 // Bump up the resend count on this packet
371 Interlocked.Increment(ref outgoingPacket.ResendCount);
372 //Interlocked.Increment(ref Stats.ResentPackets);
373
374 // Queue or (re)send the packet
375 if (!outgoingPacket.Client.EnqueueOutgoing(outgoingPacket))
376 SendPacketFinal(outgoingPacket);
377 }
378 else
379 {
380 m_log.DebugFormat("[LLUDPSERVER]: Dropping packet #{0} for agent {1} after {2} failed attempts",
381 outgoingPacket.SequenceNumber, outgoingPacket.Client.RemoteEndPoint, outgoingPacket.ResendCount);
382
383 lock (client.NeedAcks.SyncRoot)
384 client.NeedAcks.RemoveUnsafe(outgoingPacket.SequenceNumber);
385
386 //Interlocked.Increment(ref Stats.DroppedPackets);
387
388 // Disconnect an agent if no packets are received for some time
389 //FIXME: Make 60 an .ini setting
390 if (Environment.TickCount - client.TickLastPacketReceived > 1000 * 60)
391 {
392 m_log.Warn("[LLUDPSERVER]: Ack timeout, disconnecting " + client.ClientAPI.Name);
393
394 RemoveClient(client.ClientAPI);
395 return;
396 }
397 }
336 } 398 }
337 } 399 }
338 catch (ObjectDisposedException)
339 {
340 m_log.Info(
341 "[UDPSERVER]: UDP Object disposed. No need to worry about this if you're restarting the simulator.");
342
343 done = true;
344 }
345 catch (Exception ex)
346 {
347 m_log.ErrorFormat("[CLIENT]: Exception thrown during BeginReceive(): {0}", ex);
348 }
349 } 400 }
350 } 401 }
351 402
352 /// <summary> 403 public void Flush()
353 /// Close a client circuit. This is done in response to an exception on receive, and should not be called
354 /// normally.
355 /// </summary>
356 /// <param name="e">The exception that caused the close. Can be null if there was no exception</param>
357 private void CloseCircuit(Exception e)
358 { 404 {
359 uint circuit; 405 // FIXME: Implement?
360 lock (clientCircuits) 406 }
407
408 internal void SendPacketFinal(OutgoingPacket outgoingPacket)
409 {
410 UDPPacketBuffer buffer = outgoingPacket.Buffer;
411 byte flags = buffer.Data[0];
412 bool isResend = (flags & Helpers.MSG_RESENT) != 0;
413 bool isReliable = (flags & Helpers.MSG_RELIABLE) != 0;
414 LLUDPClient client = outgoingPacket.Client;
415
416 // Keep track of when this packet was sent out (right now)
417 outgoingPacket.TickCount = Environment.TickCount;
418
419 #region ACK Appending
420
421 int dataLength = buffer.DataLength;
422
423 // Keep appending ACKs until there is no room left in the packet or there are
424 // no more ACKs to append
425 uint ackCount = 0;
426 uint ack;
427 while (dataLength + 5 < buffer.Data.Length && client.PendingAcks.Dequeue(out ack))
428 {
429 Utils.UIntToBytesBig(ack, buffer.Data, dataLength);
430 dataLength += 4;
431 ++ackCount;
432 }
433
434 if (ackCount > 0)
435 {
436 // Set the last byte of the packet equal to the number of appended ACKs
437 buffer.Data[dataLength++] = (byte)ackCount;
438 // Set the appended ACKs flag on this packet
439 buffer.Data[0] = (byte)(buffer.Data[0] | Helpers.MSG_APPENDED_ACKS);
440 }
441
442 buffer.DataLength = dataLength;
443
444 #endregion ACK Appending
445
446 if (!isResend)
361 { 447 {
362 if (clientCircuits.TryGetValue(reusedEpSender, out circuit)) 448 // Not a resend, assign a new sequence number
449 uint sequenceNumber = (uint)Interlocked.Increment(ref client.CurrentSequence);
450 Utils.UIntToBytesBig(sequenceNumber, buffer.Data, 1);
451 outgoingPacket.SequenceNumber = sequenceNumber;
452
453 if (isReliable)
363 { 454 {
364 m_packetServer.CloseCircuit(circuit); 455 // Add this packet to the list of ACK responses we are waiting on from the server
365 456 client.NeedAcks.Add(outgoingPacket);
366 if (e != null)
367 m_log.ErrorFormat(
368 "[CLIENT]: Closed circuit {0} {1} due to exception {2}", circuit, reusedEpSender, e);
369 } 457 }
370 } 458 }
459
460 // Stats tracking
461 Interlocked.Increment(ref client.PacketsSent);
462 if (isReliable)
463 Interlocked.Add(ref client.UnackedBytes, outgoingPacket.Buffer.DataLength);
464
465 // Put the UDP payload on the wire
466 AsyncBeginSend(buffer);
371 } 467 }
372 468
373 /// <summary> 469 protected override void PacketReceived(UDPPacketBuffer buffer)
374 /// Finish the process of asynchronously receiving the next bit of raw data
375 /// </summary>
376 /// <param name="numBytes">The number of bytes received. Will return 0 if no bytes were recieved
377 /// <param name="result"></param>
378 /// <param name="epSender">The sender of the data</param>
379 /// <returns></returns>
380 protected virtual bool EndReceive(out int numBytes, IAsyncResult result, ref EndPoint epSender)
381 { 470 {
382 bool hasReceivedOkay = false; 471 // Debugging/Profiling
383 numBytes = 0; 472 //try { Thread.CurrentThread.Name = "PacketReceived (" + scene.RegionName + ")"; }
384 473 //catch (Exception) { }
474
475 LLUDPClient client = null;
476 Packet packet = null;
477 int packetEnd = buffer.DataLength - 1;
478 IPEndPoint address = (IPEndPoint)buffer.RemoteEndPoint;
479
480 #region Decoding
481
385 try 482 try
386 { 483 {
387 numBytes = m_socket.EndReceiveFrom(result, ref epSender); 484 packet = Packet.BuildPacket(buffer.Data, ref packetEnd,
388 hasReceivedOkay = true; 485 // Only allocate a buffer for zerodecoding if the packet is zerocoded
486 ((buffer.Data[0] & Helpers.MSG_ZEROCODED) != 0) ? new byte[4096] : null);
389 } 487 }
390 catch (SocketException e) 488 catch (MalformedDataException)
391 { 489 {
392 // TODO : Actually only handle those states that we have control over, re-throw everything else, 490 m_log.ErrorFormat("[LLUDPSERVER]: Malformed data, cannot parse packet:\n{0}",
393 // TODO: implement cases as we encounter them. 491 Utils.BytesToHexString(buffer.Data, buffer.DataLength, null));
394 //m_log.Error("[CLIENT]: Connection Error! - " + e.ToString()); 492 }
395 switch (e.SocketErrorCode) 493
494 // Fail-safe check
495 if (packet == null)
496 {
497 m_log.Warn("[LLUDPSERVER]: Couldn't build a message from the incoming data");
498 return;
499 }
500
501 //Stats.RecvBytes += (ulong)buffer.DataLength;
502 //++Stats.RecvPackets;
503
504 #endregion Decoding
505
506 #region UseCircuitCode Handling
507
508 if (packet.Type == PacketType.UseCircuitCode)
509 {
510 UseCircuitCodePacket useCircuitCode = (UseCircuitCodePacket)packet;
511 IClientAPI newuser;
512 uint circuitCode = useCircuitCode.CircuitCode.Code;
513
514 // Check if the client is already established
515 if (!m_scene.ClientManager.TryGetClient(circuitCode, out newuser))
396 { 516 {
397 case SocketError.AlreadyInProgress: 517 AddNewClient(useCircuitCode, (IPEndPoint)buffer.RemoteEndPoint);
398 return hasReceivedOkay; 518 }
519 }
520
521 // Determine which agent this packet came from
522 if (!clients.TryGetValue(address, out client))
523 {
524 m_log.Warn("[LLUDPSERVER]: Received a " + packet.Type + " packet from an unrecognized source: " + address);
525 return;
526 }
527
528 #endregion UseCircuitCode Handling
529
530 // Stats tracking
531 Interlocked.Increment(ref client.PacketsReceived);
399 532
400 case SocketError.NetworkReset: 533 #region ACK Receiving
401 case SocketError.ConnectionReset:
402 case SocketError.OperationAborted:
403 break;
404 534
405 default: 535 int now = Environment.TickCount;
406 throw; 536 client.TickLastPacketReceived = now;
537
538 // Handle appended ACKs
539 if (packet.Header.AppendedAcks && packet.Header.AckList != null)
540 {
541 lock (client.NeedAcks.SyncRoot)
542 {
543 for (int i = 0; i < packet.Header.AckList.Length; i++)
544 AcknowledgePacket(client, packet.Header.AckList[i], now, packet.Header.Resent);
407 } 545 }
408 } 546 }
409 catch (ObjectDisposedException e) 547
548 // Handle PacketAck packets
549 if (packet.Type == PacketType.PacketAck)
550 {
551 PacketAckPacket ackPacket = (PacketAckPacket)packet;
552
553 lock (client.NeedAcks.SyncRoot)
554 {
555 for (int i = 0; i < ackPacket.Packets.Length; i++)
556 AcknowledgePacket(client, ackPacket.Packets[i].ID, now, packet.Header.Resent);
557 }
558 }
559
560 #endregion ACK Receiving
561
562 #region ACK Sending
563
564 if (packet.Header.Reliable)
565 client.PendingAcks.Enqueue((uint)packet.Header.Sequence);
566
567 // This is a somewhat odd sequence of steps to pull the client.BytesSinceLastACK value out,
568 // add the current received bytes to it, test if 2*MTU bytes have been sent, if so remove
569 // 2*MTU bytes from the value and send ACKs, and finally add the local value back to
570 // client.BytesSinceLastACK. Lockless thread safety
571 int bytesSinceLastACK = Interlocked.Exchange(ref client.BytesSinceLastACK, 0);
572 bytesSinceLastACK += buffer.DataLength;
573 if (bytesSinceLastACK > Packet.MTU * 2)
574 {
575 bytesSinceLastACK -= Packet.MTU * 2;
576 SendAcks(client);
577 }
578 Interlocked.Add(ref client.BytesSinceLastACK, bytesSinceLastACK);
579
580 #endregion ACK Sending
581
582 #region Incoming Packet Accounting
583
584 // Check the archive of received reliable packet IDs to see whether we already received this packet
585 if (packet.Header.Reliable && !client.PacketArchive.TryEnqueue(packet.Header.Sequence))
410 { 586 {
411 m_log.DebugFormat("[CLIENT]: ObjectDisposedException: Object {0} disposed.", e.ObjectName); 587 if (packet.Header.Resent)
412 // Uhh, what object, and why? this needs better handling. 588 m_log.Debug("[LLUDPSERVER]: Received a resend of already processed packet #" + packet.Header.Sequence + ", type: " + packet.Type);
589 else
590 m_log.Warn("[LLUDPSERVER]: Received a duplicate (not marked as resend) of packet #" + packet.Header.Sequence + ", type: " + packet.Type);
591
592 // Avoid firing a callback twice for the same packet
593 return;
594 }
595
596 #endregion Incoming Packet Accounting
597
598 // Don't bother clogging up the queue with PacketAck packets that are already handled here
599 if (packet.Type != PacketType.PacketAck)
600 {
601 // Inbox insertion
602 packetInbox.Enqueue(new IncomingPacket(client, packet));
413 } 603 }
414
415 return hasReceivedOkay;
416 } 604 }
417 605
418 /// <summary> 606 protected override void PacketSent(UDPPacketBuffer buffer, int bytesSent)
419 /// Add a new client circuit. 607 {
420 /// </summary> 608 }
421 /// <param name="packet"></param> 609
422 /// <param name="epSender"></param> 610 private bool IsClientAuthorized(UseCircuitCodePacket useCircuitCode, out AuthenticateResponse sessionInfo)
423 /// <param name="epProxy"></param> 611 {
424 protected virtual void AddNewClient(UseCircuitCodePacket useCircuit, EndPoint epSender, EndPoint epProxy) 612 UUID agentID = useCircuitCode.CircuitCode.ID;
613 UUID sessionID = useCircuitCode.CircuitCode.SessionID;
614 uint circuitCode = useCircuitCode.CircuitCode.Code;
615
616 sessionInfo = m_circuitManager.AuthenticateSession(sessionID, agentID, circuitCode);
617 return sessionInfo.Authorised;
618 }
619
620 private void AddNewClient(UseCircuitCodePacket useCircuitCode, IPEndPoint remoteEndPoint)
425 { 621 {
426 //Slave regions don't accept new clients 622 //Slave regions don't accept new clients
427 if (m_localScene.RegionStatus != RegionStatus.SlaveScene) 623 if (m_scene.RegionStatus != RegionStatus.SlaveScene)
428 { 624 {
429 AuthenticateResponse sessionInfo; 625 AuthenticateResponse sessionInfo;
430 bool isNewCircuit = false; 626 bool isNewCircuit = !clients.ContainsKey(remoteEndPoint);
431 627
432 if (!m_packetServer.IsClientAuthorized(useCircuit, m_circuitManager, out sessionInfo)) 628 if (!IsClientAuthorized(useCircuitCode, out sessionInfo))
433 { 629 {
434 m_log.WarnFormat( 630 m_log.WarnFormat(
435 "[CONNECTION FAILURE]: Connection request for client {0} connecting with unnotified circuit code {1} from {2}", 631 "[CONNECTION FAILURE]: Connection request for client {0} connecting with unnotified circuit code {1} from {2}",
436 useCircuit.CircuitCode.ID, useCircuit.CircuitCode.Code, epSender); 632 useCircuitCode.CircuitCode.ID, useCircuitCode.CircuitCode.Code, remoteEndPoint);
437
438 return; 633 return;
439 } 634 }
440
441 lock (clientCircuits)
442 {
443 if (!clientCircuits.ContainsKey(epSender))
444 {
445 clientCircuits.Add(epSender, useCircuit.CircuitCode.Code);
446 isNewCircuit = true;
447 }
448 }
449 635
450 if (isNewCircuit) 636 if (isNewCircuit)
451 { 637 {
452 // This doesn't need locking as it's synchronized data 638 UUID agentID = useCircuitCode.CircuitCode.ID;
453 clientCircuits_reverse[useCircuit.CircuitCode.Code] = epSender; 639 UUID sessionID = useCircuitCode.CircuitCode.SessionID;
640 uint circuitCode = useCircuitCode.CircuitCode.Code;
454 641
455 lock (proxyCircuits) 642 AddClient(circuitCode, agentID, sessionID, remoteEndPoint, sessionInfo);
456 {
457 proxyCircuits[useCircuit.CircuitCode.Code] = epProxy;
458 }
459
460 m_packetServer.AddNewClient(epSender, useCircuit, sessionInfo, epProxy);
461
462 //m_log.DebugFormat(
463 // "[CONNECTION SUCCESS]: Incoming client {0} (circuit code {1}) received and authenticated for {2}",
464 // useCircuit.CircuitCode.ID, useCircuit.CircuitCode.Code, m_localScene.RegionInfo.RegionName);
465 } 643 }
466 } 644 }
467
468 // Ack the UseCircuitCode packet
469 PacketAckPacket ack_it = (PacketAckPacket)PacketPool.Instance.GetPacket(PacketType.PacketAck);
470 // TODO: don't create new blocks if recycling an old packet
471 ack_it.Packets = new PacketAckPacket.PacketsBlock[1];
472 ack_it.Packets[0] = new PacketAckPacket.PacketsBlock();
473 ack_it.Packets[0].ID = useCircuit.Header.Sequence;
474 // ((useCircuit.Header.Sequence < uint.MaxValue) ? useCircuit.Header.Sequence : 0) is just a failsafe to ensure that we don't overflow.
475 ack_it.Header.Sequence = ((useCircuit.Header.Sequence < uint.MaxValue) ? useCircuit.Header.Sequence : 0) + 1;
476 ack_it.Header.Reliable = false;
477
478 byte[] ackmsg = ack_it.ToBytes();
479
480 // Need some extra space in case we need to add proxy
481 // information to the message later
482 byte[] msg = new byte[4096];
483 Buffer.BlockCopy(ackmsg, 0, msg, 0, ackmsg.Length);
484
485 SendPacketTo(msg, ackmsg.Length, SocketFlags.None, useCircuit.CircuitCode.Code);
486
487 PacketPool.Instance.ReturnPacket(useCircuit);
488 } 645 }
489 646
490 public void ServerListener() 647 private void AddClient(uint circuitCode, UUID agentID, UUID sessionID, IPEndPoint remoteEndPoint, AuthenticateResponse sessionInfo)
491 { 648 {
492 uint newPort = listenPort; 649 // Create the LLUDPClient
493 m_log.Info("[UDPSERVER]: Opening UDP socket on " + listenIP + " " + newPort + "."); 650 LLUDPClient client = new LLUDPClient(this, m_throttleRates, m_throttle, circuitCode, agentID, remoteEndPoint);
494
495 ServerIncoming = new IPEndPoint(listenIP, (int)newPort);
496 m_socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
497 if (0 != m_clientSocketReceiveBuffer)
498 m_socket.ReceiveBufferSize = m_clientSocketReceiveBuffer;
499 m_socket.Bind(ServerIncoming);
500 // Add flags to the UDP socket to prevent "Socket forcibly closed by host"
501 // uint IOC_IN = 0x80000000;
502 // uint IOC_VENDOR = 0x18000000;
503 // uint SIO_UDP_CONNRESET = IOC_IN | IOC_VENDOR | 12;
504 // TODO: this apparently works in .NET but not in Mono, need to sort out the right flags here.
505 // m_socket.IOControl((int)SIO_UDP_CONNRESET, new byte[] { Convert.ToByte(false) }, null);
506 651
507 listenPort = newPort; 652 // Create the LLClientView
653 LLClientView clientApi = new LLClientView(remoteEndPoint, m_scene, this, client, sessionInfo, agentID, sessionID, circuitCode);
654 clientApi.OnViewerEffect += m_scene.ClientManager.ViewerEffectHandler;
655 clientApi.OnLogout += LogoutHandler;
656 clientApi.OnConnectionClosed += RemoveClient;
508 657
509 m_log.Info("[UDPSERVER]: UDP socket bound, getting ready to listen"); 658 // Start the IClientAPI
659 m_scene.ClientManager.Add(circuitCode, clientApi);
660 clientApi.Start();
510 661
511 ReceivedData = OnReceivedData; 662 // Give LLUDPClient a reference to IClientAPI
512 BeginReceive(); 663 client.ClientAPI = clientApi;
513 664
514 m_log.Info("[UDPSERVER]: Listening on port " + newPort); 665 // Add the new client to our list of tracked clients
666 clients.Add(agentID, client.RemoteEndPoint, client);
515 } 667 }
516 668
517 public virtual void RegisterPacketServer(LLPacketServer server) 669 private void AcknowledgePacket(LLUDPClient client, uint ack, int currentTime, bool fromResend)
518 { 670 {
519 m_packetServer = server; 671 OutgoingPacket ackedPacket;
672 if (client.NeedAcks.RemoveUnsafe(ack, out ackedPacket) && !fromResend)
673 {
674 // Update stats
675 Interlocked.Add(ref client.UnackedBytes, -ackedPacket.Buffer.DataLength);
676
677 // Calculate the round-trip time for this packet and its ACK
678 int rtt = currentTime - ackedPacket.TickCount;
679 if (rtt > 0)
680 client.UpdateRoundTrip(rtt);
681 }
520 } 682 }
521 683
522 public virtual void SendPacketTo(byte[] buffer, int size, SocketFlags flags, uint circuitcode) 684 private void IncomingPacketHandler()
523 //EndPoint packetSender)
524 { 685 {
525 // find the endpoint for this circuit 686 // Set this culture for the thread that incoming packets are received
526 EndPoint sendto; 687 // on to en-US to avoid number parsing issues
527 try 688 Culture.SetCurrentCulture();
528 {
529 sendto = (EndPoint)clientCircuits_reverse[circuitcode];
530 }
531 catch
532 {
533 // Exceptions here mean there is no circuit
534 m_log.Warn("[CLIENT]: Circuit not found, not sending packet");
535 return;
536 }
537 689
538 if (sendto != null) 690 IncomingPacket incomingPacket = null;
691
692 while (base.IsRunning)
539 { 693 {
540 //we found the endpoint so send the packet to it 694 if (packetInbox.Dequeue(100, ref incomingPacket))
541 if (proxyPortOffset != 0) 695 Util.FireAndForget(ProcessInPacket, incomingPacket);
542 {
543 //MainLog.Instance.Verbose("UDPSERVER", "SendPacketTo proxy " + proxyCircuits[circuitcode].ToString() + ": client " + sendto.ToString());
544 ProxyCodec.EncodeProxyMessage(buffer, ref size, sendto);
545 m_socket.SendTo(buffer, size, flags, proxyCircuits[circuitcode]);
546 }
547 else
548 {
549 //MainLog.Instance.Verbose("UDPSERVER", "SendPacketTo : client " + sendto.ToString());
550 try
551 {
552 m_socket.SendTo(buffer, size, flags, sendto);
553 }
554 catch (SocketException SockE)
555 {
556 m_log.ErrorFormat("[UDPSERVER]: Caught Socket Error in the send buffer!. {0}",SockE.ToString());
557 }
558 }
559 } 696 }
697
698 if (packetInbox.Count > 0)
699 m_log.Warn("[LLUDPSERVER]: IncomingPacketHandler is shutting down, dropping " + packetInbox.Count + " packets");
700 packetInbox.Clear();
560 } 701 }
561 702
562 public virtual void RemoveClientCircuit(uint circuitcode) 703 private void OutgoingPacketHandler()
563 { 704 {
564 EndPoint sendto; 705 // Set this culture for the thread that outgoing packets are sent
565 if (clientCircuits_reverse.Contains(circuitcode)) 706 // on to en-US to avoid number parsing issues
707 Culture.SetCurrentCulture();
708
709 int now = Environment.TickCount;
710 int elapsedMS = 0;
711 int elapsed100MS = 0;
712 int elapsed500MS = 0;
713
714 while (base.IsRunning)
566 { 715 {
567 sendto = (EndPoint)clientCircuits_reverse[circuitcode]; 716 bool resendUnacked = false;
717 bool sendAcks = false;
718 bool sendPings = false;
719 bool packetSent = false;
568 720
569 clientCircuits_reverse.Remove(circuitcode); 721 elapsedMS += Environment.TickCount - now;
570 722
571 lock (clientCircuits) 723 // Check for pending outgoing resends every 100ms
724 if (elapsedMS >= 100)
572 { 725 {
573 if (sendto != null) 726 resendUnacked = true;
574 { 727 elapsedMS -= 100;
575 clientCircuits.Remove(sendto); 728 ++elapsed100MS;
576 }
577 else
578 {
579 m_log.DebugFormat(
580 "[CLIENT]: endpoint for circuit code {0} in RemoveClientCircuit() was unexpectedly null!", circuitcode);
581 }
582 } 729 }
583 lock (proxyCircuits) 730 // Check for pending outgoing ACKs every 500ms
731 if (elapsed100MS >= 5)
584 { 732 {
585 proxyCircuits.Remove(circuitcode); 733 sendAcks = true;
734 elapsed100MS = 0;
735 ++elapsed500MS;
586 } 736 }
737 // Send pings to clients every 5000ms
738 if (elapsed500MS >= 10)
739 {
740 sendPings = true;
741 elapsed500MS = 0;
742 }
743
744 clients.ForEach(
745 delegate(LLUDPClient client)
746 {
747 if (client.DequeueOutgoing())
748 packetSent = true;
749 if (resendUnacked)
750 ResendUnacked(client);
751 if (sendAcks)
752 {
753 SendAcks(client);
754 client.SendPacketStats();
755 }
756 if (sendPings)
757 SendPing(client);
758 }
759 );
760
761 if (!packetSent)
762 Thread.Sleep(20);
587 } 763 }
588 } 764 }
589 765
590 public void RestoreClient(AgentCircuitData circuit, EndPoint userEP, EndPoint proxyEP) 766 private void ProcessInPacket(object state)
591 { 767 {
592 //MainLog.Instance.Verbose("UDPSERVER", "RestoreClient"); 768 IncomingPacket incomingPacket = (IncomingPacket)state;
769 Packet packet = incomingPacket.Packet;
770 LLUDPClient client = incomingPacket.Client;
593 771
594 UseCircuitCodePacket useCircuit = new UseCircuitCodePacket(); 772 // Sanity check
595 useCircuit.CircuitCode.Code = circuit.circuitcode; 773 if (packet == null || client == null || client.ClientAPI == null)
596 useCircuit.CircuitCode.ID = circuit.AgentID;
597 useCircuit.CircuitCode.SessionID = circuit.SessionID;
598
599 AuthenticateResponse sessionInfo;
600
601 if (!m_packetServer.IsClientAuthorized(useCircuit, m_circuitManager, out sessionInfo))
602 { 774 {
603 m_log.WarnFormat( 775 m_log.WarnFormat("[LLUDPSERVER]: Processing a packet with incomplete state. Packet=\"{0}\", Client=\"{1}\", Client.ClientAPI=\"{2}\"",
604 "[CLIENT]: Restore request denied to avatar {0} connecting with unauthorized circuit code {1}", 776 packet, client, (client != null) ? client.ClientAPI : null);
605 useCircuit.CircuitCode.ID, useCircuit.CircuitCode.Code);
606
607 return;
608 } 777 }
609 778
610 lock (clientCircuits) 779 try
611 { 780 {
612 if (!clientCircuits.ContainsKey(userEP)) 781 // Process this packet
613 clientCircuits.Add(userEP, useCircuit.CircuitCode.Code); 782 client.ClientAPI.ProcessInPacket(packet);
614 else
615 m_log.Error("[CLIENT]: clientCircuits already contains entry for user " + useCircuit.CircuitCode.Code + ". NOT adding.");
616 } 783 }
617 784 catch (ThreadAbortException)
618 // This data structure is synchronized, so we don't need the lock
619 if (!clientCircuits_reverse.ContainsKey(useCircuit.CircuitCode.Code))
620 clientCircuits_reverse.Add(useCircuit.CircuitCode.Code, userEP);
621 else
622 m_log.Error("[CLIENT]: clientCurcuits_reverse already contains entry for user " + useCircuit.CircuitCode.Code + ". NOT adding.");
623
624 lock (proxyCircuits)
625 { 785 {
626 if (!proxyCircuits.ContainsKey(useCircuit.CircuitCode.Code)) 786 // If something is trying to abort the packet processing thread, take that as a hint that it's time to shut down
627 { 787 m_log.Info("[LLUDPSERVER]: Caught a thread abort, shutting down the LLUDP server");
628 proxyCircuits.Add(useCircuit.CircuitCode.Code, proxyEP); 788 Stop();
629 }
630 else
631 {
632 // re-set proxy endpoint
633 proxyCircuits.Remove(useCircuit.CircuitCode.Code);
634 proxyCircuits.Add(useCircuit.CircuitCode.Code, proxyEP);
635 }
636 } 789 }
790 catch (Exception e)
791 {
792 // Don't let a failure in an individual client thread crash the whole sim.
793 m_log.ErrorFormat("[LLUDPSERVER]: Client packet handler for {0} for packet {1} threw an exception", client.AgentID, packet.Type);
794 m_log.Error(e.Message, e);
795 }
796 }
637 797
638 m_packetServer.AddNewClient(userEP, useCircuit, sessionInfo, proxyEP); 798 private void LogoutHandler(IClientAPI client)
799 {
800 client.SendLogoutPacket();
801 RemoveClient(client);
639 } 802 }
640 } 803 }
641} 804}