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