diff options
Diffstat (limited to 'OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs')
-rw-r--r-- | OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs | 197 |
1 files changed, 148 insertions, 49 deletions
diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs index 75f783b..9cce725 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs | |||
@@ -147,21 +147,34 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
147 | private int m_elapsed500MSOutgoingPacketHandler; | 147 | private int m_elapsed500MSOutgoingPacketHandler; |
148 | 148 | ||
149 | /// <summary>Flag to signal when clients should check for resends</summary> | 149 | /// <summary>Flag to signal when clients should check for resends</summary> |
150 | private bool m_resendUnacked; | 150 | protected bool m_resendUnacked; |
151 | |||
151 | /// <summary>Flag to signal when clients should send ACKs</summary> | 152 | /// <summary>Flag to signal when clients should send ACKs</summary> |
152 | private bool m_sendAcks; | 153 | protected bool m_sendAcks; |
154 | |||
153 | /// <summary>Flag to signal when clients should send pings</summary> | 155 | /// <summary>Flag to signal when clients should send pings</summary> |
154 | private bool m_sendPing; | 156 | protected bool m_sendPing; |
155 | 157 | ||
156 | private int m_defaultRTO = 0; | 158 | private int m_defaultRTO = 0; |
157 | private int m_maxRTO = 0; | 159 | private int m_maxRTO = 0; |
158 | 160 | private int m_ackTimeout = 0; | |
161 | private int m_pausedAckTimeout = 0; | ||
159 | private bool m_disableFacelights = false; | 162 | private bool m_disableFacelights = false; |
160 | 163 | ||
161 | public Socket Server { get { return null; } } | 164 | public Socket Server { get { return null; } } |
162 | 165 | ||
163 | private int m_malformedCount = 0; // Guard against a spamming attack | 166 | private int m_malformedCount = 0; // Guard against a spamming attack |
164 | 167 | ||
168 | /// <summary> | ||
169 | /// Record current outgoing client for monitoring purposes. | ||
170 | /// </summary> | ||
171 | private IClientAPI m_currentOutgoingClient; | ||
172 | |||
173 | /// <summary> | ||
174 | /// Recording current incoming client for monitoring purposes. | ||
175 | /// </summary> | ||
176 | private IClientAPI m_currentIncomingClient; | ||
177 | |||
165 | public LLUDPServer(IPAddress listenIP, ref uint port, int proxyPortOffsetParm, bool allow_alternate_port, IConfigSource configSource, AgentCircuitManager circuitManager) | 178 | public LLUDPServer(IPAddress listenIP, ref uint port, int proxyPortOffsetParm, bool allow_alternate_port, IConfigSource configSource, AgentCircuitManager circuitManager) |
166 | : base(listenIP, (int)port) | 179 | : base(listenIP, (int)port) |
167 | { | 180 | { |
@@ -198,11 +211,15 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
198 | m_defaultRTO = config.GetInt("DefaultRTO", 0); | 211 | m_defaultRTO = config.GetInt("DefaultRTO", 0); |
199 | m_maxRTO = config.GetInt("MaxRTO", 0); | 212 | m_maxRTO = config.GetInt("MaxRTO", 0); |
200 | m_disableFacelights = config.GetBoolean("DisableFacelights", false); | 213 | m_disableFacelights = config.GetBoolean("DisableFacelights", false); |
214 | m_ackTimeout = 1000 * config.GetInt("AckTimeout", 60); | ||
215 | m_pausedAckTimeout = 1000 * config.GetInt("PausedAckTimeout", 300); | ||
201 | } | 216 | } |
202 | else | 217 | else |
203 | { | 218 | { |
204 | PrimUpdatesPerCallback = 100; | 219 | PrimUpdatesPerCallback = 100; |
205 | TextureSendLimit = 20; | 220 | TextureSendLimit = 20; |
221 | m_ackTimeout = 1000 * 60; // 1 minute | ||
222 | m_pausedAckTimeout = 1000 * 300; // 5 minutes | ||
206 | } | 223 | } |
207 | 224 | ||
208 | #region BinaryStats | 225 | #region BinaryStats |
@@ -239,19 +256,56 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
239 | if (m_scene == null) | 256 | if (m_scene == null) |
240 | throw new InvalidOperationException("[LLUDPSERVER]: Cannot LLUDPServer.Start() without an IScene reference"); | 257 | throw new InvalidOperationException("[LLUDPSERVER]: Cannot LLUDPServer.Start() without an IScene reference"); |
241 | 258 | ||
242 | m_log.Info("[LLUDPSERVER]: Starting the LLUDP server in " + (m_asyncPacketHandling ? "asynchronous" : "synchronous") + " mode"); | 259 | m_log.InfoFormat( |
260 | "[LLUDPSERVER]: Starting the LLUDP server in {0} mode", | ||
261 | m_asyncPacketHandling ? "asynchronous" : "synchronous"); | ||
243 | 262 | ||
244 | base.Start(m_recvBufferSize, m_asyncPacketHandling); | 263 | base.Start(m_recvBufferSize, m_asyncPacketHandling); |
245 | 264 | ||
246 | // Start the packet processing threads | 265 | // Start the packet processing threads |
247 | Watchdog.StartThread( | 266 | Watchdog.StartThread( |
248 | IncomingPacketHandler, "Incoming Packets (" + m_scene.RegionInfo.RegionName + ")", ThreadPriority.Normal, false, true); | 267 | IncomingPacketHandler, |
268 | string.Format("Incoming Packets ({0})", m_scene.RegionInfo.RegionName), | ||
269 | ThreadPriority.Normal, | ||
270 | false, | ||
271 | true, | ||
272 | GetWatchdogIncomingAlarmData, | ||
273 | Watchdog.WATCHDOG_TIMEOUT_MS); | ||
274 | |||
249 | Watchdog.StartThread( | 275 | Watchdog.StartThread( |
250 | OutgoingPacketHandler, "Outgoing Packets (" + m_scene.RegionInfo.RegionName + ")", ThreadPriority.Normal, false, true); | 276 | OutgoingPacketHandler, |
277 | string.Format("Outgoing Packets ({0})", m_scene.RegionInfo.RegionName), | ||
278 | ThreadPriority.Normal, | ||
279 | false, | ||
280 | true, | ||
281 | GetWatchdogOutgoingAlarmData, | ||
282 | Watchdog.WATCHDOG_TIMEOUT_MS); | ||
251 | 283 | ||
252 | m_elapsedMSSinceLastStatReport = Environment.TickCount; | 284 | m_elapsedMSSinceLastStatReport = Environment.TickCount; |
253 | } | 285 | } |
254 | 286 | ||
287 | /// <summary> | ||
288 | /// If the outgoing UDP thread times out, then return client that was being processed to help with debugging. | ||
289 | /// </summary> | ||
290 | /// <returns></returns> | ||
291 | private string GetWatchdogIncomingAlarmData() | ||
292 | { | ||
293 | return string.Format( | ||
294 | "Client is {0}", | ||
295 | m_currentIncomingClient != null ? m_currentIncomingClient.Name : "none"); | ||
296 | } | ||
297 | |||
298 | /// <summary> | ||
299 | /// If the outgoing UDP thread times out, then return client that was being processed to help with debugging. | ||
300 | /// </summary> | ||
301 | /// <returns></returns> | ||
302 | private string GetWatchdogOutgoingAlarmData() | ||
303 | { | ||
304 | return string.Format( | ||
305 | "Client is {0}", | ||
306 | m_currentOutgoingClient != null ? m_currentOutgoingClient.Name : "none"); | ||
307 | } | ||
308 | |||
255 | public new void Stop() | 309 | public new void Stop() |
256 | { | 310 | { |
257 | m_log.Info("[LLUDPSERVER]: Shutting down the LLUDP server for " + m_scene.RegionInfo.RegionName); | 311 | m_log.Info("[LLUDPSERVER]: Shutting down the LLUDP server for " + m_scene.RegionInfo.RegionName); |
@@ -485,19 +539,34 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
485 | SendPacket(udpClient, completePing, ThrottleOutPacketType.Unknown, false, null); | 539 | SendPacket(udpClient, completePing, ThrottleOutPacketType.Unknown, false, null); |
486 | } | 540 | } |
487 | 541 | ||
488 | public void HandleUnacked(LLUDPClient udpClient) | 542 | public void HandleUnacked(LLClientView client) |
489 | { | 543 | { |
544 | LLUDPClient udpClient = client.UDPClient; | ||
545 | |||
490 | if (!udpClient.IsConnected) | 546 | if (!udpClient.IsConnected) |
491 | return; | 547 | return; |
492 | 548 | ||
493 | // Disconnect an agent if no packets are received for some time | 549 | // Disconnect an agent if no packets are received for some time |
494 | //FIXME: Make 60 an .ini setting | 550 | int timeoutTicks = m_ackTimeout; |
495 | if ((Environment.TickCount & Int32.MaxValue) - udpClient.TickLastPacketReceived > 1000 * 60) | 551 | |
552 | // Allow more slack if the client is "paused" eg file upload dialogue is open | ||
553 | // Some sort of limit is needed in case the client crashes, loses its network connection | ||
554 | // or some other disaster prevents it from sendung the AgentResume | ||
555 | if (udpClient.IsPaused) | ||
556 | timeoutTicks = m_pausedAckTimeout; | ||
557 | |||
558 | if (client.IsActive && | ||
559 | (Environment.TickCount & Int32.MaxValue) - udpClient.TickLastPacketReceived > timeoutTicks) | ||
496 | { | 560 | { |
497 | m_log.Warn("[LLUDPSERVER]: Ack timeout, disconnecting " + udpClient.AgentID); | 561 | // We must set IsActive synchronously so that we can stop the packet loop reinvoking this method, even |
498 | StatsManager.SimExtraStats.AddAbnormalClientThreadTermination(); | 562 | // though it's set later on by LLClientView.Close() |
563 | client.IsActive = false; | ||
564 | |||
565 | // Fire this out on a different thread so that we don't hold up outgoing packet processing for | ||
566 | // everybody else if this is being called due to an ack timeout. | ||
567 | // This is the same as processing as the async process of a logout request. | ||
568 | Util.FireAndForget(o => DeactivateClientDueToTimeout(client)); | ||
499 | 569 | ||
500 | RemoveClient(udpClient); | ||
501 | return; | 570 | return; |
502 | } | 571 | } |
503 | 572 | ||
@@ -820,7 +889,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
820 | #endregion Ping Check Handling | 889 | #endregion Ping Check Handling |
821 | 890 | ||
822 | // Inbox insertion | 891 | // Inbox insertion |
823 | packetInbox.Enqueue(new IncomingPacket(udpClient, packet)); | 892 | packetInbox.Enqueue(new IncomingPacket((LLClientView)client, packet)); |
824 | } | 893 | } |
825 | 894 | ||
826 | #region BinaryStats | 895 | #region BinaryStats |
@@ -916,7 +985,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
916 | UDPPacketBuffer buffer = (UDPPacketBuffer)array[0]; | 985 | UDPPacketBuffer buffer = (UDPPacketBuffer)array[0]; |
917 | UseCircuitCodePacket uccp = (UseCircuitCodePacket)array[1]; | 986 | UseCircuitCodePacket uccp = (UseCircuitCodePacket)array[1]; |
918 | 987 | ||
919 | m_log.DebugFormat("[LLUDPSERVER]: Handling UseCircuitCode request from {0}", buffer.RemoteEndPoint); | 988 | m_log.DebugFormat( |
989 | "[LLUDPSERVER]: Handling UseCircuitCode request for circuit {0} to {1} from IP {2}", | ||
990 | uccp.CircuitCode.Code, m_scene.RegionInfo.RegionName, buffer.RemoteEndPoint); | ||
920 | 991 | ||
921 | remoteEndPoint = (IPEndPoint)buffer.RemoteEndPoint; | 992 | remoteEndPoint = (IPEndPoint)buffer.RemoteEndPoint; |
922 | 993 | ||
@@ -945,8 +1016,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
945 | { | 1016 | { |
946 | // Don't create clients for unauthorized requesters. | 1017 | // Don't create clients for unauthorized requesters. |
947 | m_log.WarnFormat( | 1018 | m_log.WarnFormat( |
948 | "[LLUDPSERVER]: Connection request for client {0} connecting with unnotified circuit code {1} from {2}", | 1019 | "[LLUDPSERVER]: Ignoring connection request for {0} to {1} with unknown circuit code {2} from IP {3}", |
949 | uccp.CircuitCode.ID, uccp.CircuitCode.Code, remoteEndPoint); | 1020 | uccp.CircuitCode.ID, m_scene.RegionInfo.RegionName, uccp.CircuitCode.Code, remoteEndPoint); |
950 | } | 1021 | } |
951 | 1022 | ||
952 | // m_log.DebugFormat( | 1023 | // m_log.DebugFormat( |
@@ -1044,15 +1115,30 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1044 | return client; | 1115 | return client; |
1045 | } | 1116 | } |
1046 | 1117 | ||
1047 | private void RemoveClient(LLUDPClient udpClient) | 1118 | /// <summary> |
1119 | /// Deactivates the client if we don't receive any packets within a certain amount of time (default 60 seconds). | ||
1120 | /// </summary> | ||
1121 | /// <remarks> | ||
1122 | /// If a connection is active then we will always receive packets even if nothing else is happening, due to | ||
1123 | /// regular client pings. | ||
1124 | /// </remarks> | ||
1125 | /// <param name='client'></param> | ||
1126 | private void DeactivateClientDueToTimeout(IClientAPI client) | ||
1048 | { | 1127 | { |
1049 | // Remove this client from the scene | 1128 | // We must set IsActive synchronously so that we can stop the packet loop reinvoking this method, even |
1050 | IClientAPI client; | 1129 | // though it's set later on by LLClientView.Close() |
1051 | if (m_scene.TryGetClient(udpClient.AgentID, out client)) | 1130 | client.IsActive = false; |
1052 | { | 1131 | |
1053 | client.IsLoggingOut = true; | 1132 | m_log.WarnFormat( |
1054 | client.Close(false); | 1133 | "[LLUDPSERVER]: Ack timeout, disconnecting {0} agent for {1} in {2}", |
1055 | } | 1134 | client.SceneAgent.IsChildAgent ? "child" : "root", client.Name, m_scene.RegionInfo.RegionName); |
1135 | |||
1136 | StatsManager.SimExtraStats.AddAbnormalClientThreadTermination(); | ||
1137 | |||
1138 | if (!client.SceneAgent.IsChildAgent) | ||
1139 | client.Kick("Simulator logged you out due to connection timeout"); | ||
1140 | |||
1141 | Util.FireAndForget(o => client.Close()); | ||
1056 | } | 1142 | } |
1057 | 1143 | ||
1058 | private void IncomingPacketHandler() | 1144 | private void IncomingPacketHandler() |
@@ -1161,6 +1247,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1161 | // client. m_packetSent will be set to true if a packet is sent | 1247 | // client. m_packetSent will be set to true if a packet is sent |
1162 | m_scene.ForEachClient(clientPacketHandler); | 1248 | m_scene.ForEachClient(clientPacketHandler); |
1163 | 1249 | ||
1250 | m_currentOutgoingClient = null; | ||
1251 | |||
1164 | // If nothing was sent, sleep for the minimum amount of time before a | 1252 | // If nothing was sent, sleep for the minimum amount of time before a |
1165 | // token bucket could get more tokens | 1253 | // token bucket could get more tokens |
1166 | if (!m_packetSent) | 1254 | if (!m_packetSent) |
@@ -1177,18 +1265,21 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1177 | Watchdog.RemoveThread(); | 1265 | Watchdog.RemoveThread(); |
1178 | } | 1266 | } |
1179 | 1267 | ||
1180 | private void ClientOutgoingPacketHandler(IClientAPI client) | 1268 | protected void ClientOutgoingPacketHandler(IClientAPI client) |
1181 | { | 1269 | { |
1270 | m_currentOutgoingClient = client; | ||
1271 | |||
1182 | try | 1272 | try |
1183 | { | 1273 | { |
1184 | if (client is LLClientView) | 1274 | if (client is LLClientView) |
1185 | { | 1275 | { |
1186 | LLUDPClient udpClient = ((LLClientView)client).UDPClient; | 1276 | LLClientView llClient = (LLClientView)client; |
1277 | LLUDPClient udpClient = llClient.UDPClient; | ||
1187 | 1278 | ||
1188 | if (udpClient.IsConnected) | 1279 | if (udpClient.IsConnected) |
1189 | { | 1280 | { |
1190 | if (m_resendUnacked) | 1281 | if (m_resendUnacked) |
1191 | HandleUnacked(udpClient); | 1282 | HandleUnacked(llClient); |
1192 | 1283 | ||
1193 | if (m_sendAcks) | 1284 | if (m_sendAcks) |
1194 | SendAcks(udpClient); | 1285 | SendAcks(udpClient); |
@@ -1204,8 +1295,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1204 | } | 1295 | } |
1205 | catch (Exception ex) | 1296 | catch (Exception ex) |
1206 | { | 1297 | { |
1207 | m_log.Error("[LLUDPSERVER]: OutgoingPacketHandler iteration for " + client.Name + | 1298 | m_log.Error( |
1208 | " threw an exception: " + ex.Message, ex); | 1299 | string.Format("[LLUDPSERVER]: OutgoingPacketHandler iteration for {0} threw ", client.Name), ex); |
1209 | } | 1300 | } |
1210 | } | 1301 | } |
1211 | 1302 | ||
@@ -1231,11 +1322,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1231 | { | 1322 | { |
1232 | nticks++; | 1323 | nticks++; |
1233 | watch1.Start(); | 1324 | watch1.Start(); |
1325 | m_currentOutgoingClient = client; | ||
1326 | |||
1234 | try | 1327 | try |
1235 | { | 1328 | { |
1236 | if (client is LLClientView) | 1329 | if (client is LLClientView) |
1237 | { | 1330 | { |
1238 | LLUDPClient udpClient = ((LLClientView)client).UDPClient; | 1331 | LLClientView llClient = (LLClientView)client; |
1332 | LLUDPClient udpClient = llClient.UDPClient; | ||
1239 | 1333 | ||
1240 | if (udpClient.IsConnected) | 1334 | if (udpClient.IsConnected) |
1241 | { | 1335 | { |
@@ -1244,7 +1338,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1244 | nticksUnack++; | 1338 | nticksUnack++; |
1245 | watch2.Start(); | 1339 | watch2.Start(); |
1246 | 1340 | ||
1247 | HandleUnacked(udpClient); | 1341 | HandleUnacked(llClient); |
1248 | 1342 | ||
1249 | watch2.Stop(); | 1343 | watch2.Stop(); |
1250 | avgResendUnackedTicks = (nticksUnack - 1)/(float)nticksUnack * avgResendUnackedTicks + (watch2.ElapsedTicks / (float)nticksUnack); | 1344 | avgResendUnackedTicks = (nticksUnack - 1)/(float)nticksUnack * avgResendUnackedTicks + (watch2.ElapsedTicks / (float)nticksUnack); |
@@ -1315,23 +1409,15 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1315 | 1409 | ||
1316 | #endregion | 1410 | #endregion |
1317 | 1411 | ||
1318 | private void ProcessInPacket(object state) | 1412 | private void ProcessInPacket(IncomingPacket incomingPacket) |
1319 | { | 1413 | { |
1320 | IncomingPacket incomingPacket = (IncomingPacket)state; | ||
1321 | Packet packet = incomingPacket.Packet; | 1414 | Packet packet = incomingPacket.Packet; |
1322 | LLUDPClient udpClient = incomingPacket.Client; | 1415 | LLClientView client = incomingPacket.Client; |
1323 | IClientAPI client; | ||
1324 | 1416 | ||
1325 | // Sanity check | 1417 | if (client.IsActive) |
1326 | if (packet == null || udpClient == null) | ||
1327 | { | 1418 | { |
1328 | m_log.WarnFormat("[LLUDPSERVER]: Processing a packet with incomplete state. Packet=\"{0}\", UDPClient=\"{1}\"", | 1419 | m_currentIncomingClient = client; |
1329 | packet, udpClient); | ||
1330 | } | ||
1331 | 1420 | ||
1332 | // Make sure this client is still alive | ||
1333 | if (m_scene.TryGetClient(udpClient.AgentID, out client)) | ||
1334 | { | ||
1335 | try | 1421 | try |
1336 | { | 1422 | { |
1337 | // Process this packet | 1423 | // Process this packet |
@@ -1346,21 +1432,34 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
1346 | catch (Exception e) | 1432 | catch (Exception e) |
1347 | { | 1433 | { |
1348 | // Don't let a failure in an individual client thread crash the whole sim. | 1434 | // Don't let a failure in an individual client thread crash the whole sim. |
1349 | m_log.ErrorFormat("[LLUDPSERVER]: Client packet handler for {0} for packet {1} threw an exception", udpClient.AgentID, packet.Type); | 1435 | m_log.Error( |
1350 | m_log.Error(e.Message, e); | 1436 | string.Format( |
1437 | "[LLUDPSERVER]: Client packet handler for {0} for packet {1} threw ", | ||
1438 | client.Name, packet.Type), | ||
1439 | e); | ||
1440 | } | ||
1441 | finally | ||
1442 | { | ||
1443 | m_currentIncomingClient = null; | ||
1351 | } | 1444 | } |
1352 | } | 1445 | } |
1353 | else | 1446 | else |
1354 | { | 1447 | { |
1355 | m_log.DebugFormat("[LLUDPSERVER]: Dropping incoming {0} packet for dead client {1}", packet.Type, udpClient.AgentID); | 1448 | m_log.DebugFormat( |
1449 | "[LLUDPSERVER]: Dropped incoming {0} for dead client {1} in {2}", | ||
1450 | packet.Type, client.Name, m_scene.RegionInfo.RegionName); | ||
1356 | } | 1451 | } |
1357 | } | 1452 | } |
1358 | 1453 | ||
1359 | protected void LogoutHandler(IClientAPI client) | 1454 | protected void LogoutHandler(IClientAPI client) |
1360 | { | 1455 | { |
1361 | client.SendLogoutPacket(); | 1456 | client.SendLogoutPacket(); |
1362 | if (client.IsActive) | 1457 | |
1363 | RemoveClient(((LLClientView)client).UDPClient); | 1458 | if (!client.IsLoggingOut) |
1459 | { | ||
1460 | client.IsLoggingOut = true; | ||
1461 | client.Close(); | ||
1462 | } | ||
1364 | } | 1463 | } |
1365 | } | 1464 | } |
1366 | } | 1465 | } |