clockwerk-opensim-stable – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 vero 1 /*
2 * Copyright (c) 2006, Clutch, Inc.
3 * Original Author: Jeff Cesnik
4 * All rights reserved.
5 *
6 * - Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
8 *
9 * - Redistributions of source code must retain the above copyright notice, this
10 * list of conditions and the following disclaimer.
11 * - Neither the name of the openmetaverse.org nor the names
12 * of its contributors may be used to endorse or promote products derived from
13 * this software without specific prior written permission.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
19 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
20 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
21 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
23 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
24 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25 * POSSIBILITY OF SUCH DAMAGE.
26 */
27  
28 using System;
29 using System.Net;
30 using System.Net.Sockets;
31 using System.Threading;
32 using log4net;
33 using OpenSim.Framework;
34 using OpenSim.Framework.Monitoring;
35  
36 namespace OpenMetaverse
37 {
38 /// <summary>
39 /// Base UDP server
40 /// </summary>
41 public abstract class OpenSimUDPBase
42 {
43 private static readonly ILog m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
44  
45 /// <summary>
46 /// This method is called when an incoming packet is received
47 /// </summary>
48 /// <param name="buffer">Incoming packet buffer</param>
49 public abstract void PacketReceived(UDPPacketBuffer buffer);
50  
51 /// <summary>UDP port to bind to in server mode</summary>
52 protected int m_udpPort;
53  
54 /// <summary>Local IP address to bind to in server mode</summary>
55 protected IPAddress m_localBindAddress;
56  
57 /// <summary>UDP socket, used in either client or server mode</summary>
58 private Socket m_udpSocket;
59  
60 /// <summary>Flag to process packets asynchronously or synchronously</summary>
61 private bool m_asyncPacketHandling;
62  
63 /// <summary>
64 /// Are we to use object pool(s) to reduce memory churn when receiving data?
65 /// </summary>
66 public bool UsePools { get; protected set; }
67  
68 /// <summary>
69 /// Pool to use for handling data. May be null if UsePools = false;
70 /// </summary>
71 protected OpenSim.Framework.Pool<UDPPacketBuffer> Pool { get; private set; }
72  
73 /// <summary>Returns true if the server is currently listening for inbound packets, otherwise false</summary>
74 public bool IsRunningInbound { get; private set; }
75  
76 /// <summary>Returns true if the server is currently sending outbound packets, otherwise false</summary>
77 /// <remarks>If IsRunningOut = false, then any request to send a packet is simply dropped.</remarks>
78 public bool IsRunningOutbound { get; private set; }
79  
80 /// <summary>
81 /// Number of UDP receives.
82 /// </summary>
83 public int UdpReceives { get; private set; }
84  
85 /// <summary>
86 /// Number of UDP sends
87 /// </summary>
88 public int UdpSends { get; private set; }
89  
90 /// <summary>
91 /// Number of receives over which to establish a receive time average.
92 /// </summary>
93 private readonly static int s_receiveTimeSamples = 500;
94  
95 /// <summary>
96 /// Current number of samples taken to establish a receive time average.
97 /// </summary>
98 private int m_currentReceiveTimeSamples;
99  
100 /// <summary>
101 /// Cumulative receive time for the sample so far.
102 /// </summary>
103 private int m_receiveTicksInCurrentSamplePeriod;
104  
105 /// <summary>
106 /// The average time taken for each require receive in the last sample.
107 /// </summary>
108 public float AverageReceiveTicksForLastSamplePeriod { get; private set; }
109  
110 /// <summary>
111 /// Default constructor
112 /// </summary>
113 /// <param name="bindAddress">Local IP address to bind the server to</param>
114 /// <param name="port">Port to listening for incoming UDP packets on</param>
115 /// /// <param name="usePool">Are we to use an object pool to get objects for handing inbound data?</param>
116 public OpenSimUDPBase(IPAddress bindAddress, int port)
117 {
118 m_localBindAddress = bindAddress;
119 m_udpPort = port;
120 }
121  
122 /// <summary>
123 /// Start inbound UDP packet handling.
124 /// </summary>
125 /// <param name="recvBufferSize">The size of the receive buffer for
126 /// the UDP socket. This value is passed up to the operating system
127 /// and used in the system networking stack. Use zero to leave this
128 /// value as the default</param>
129 /// <param name="asyncPacketHandling">Set this to true to start
130 /// receiving more packets while current packet handler callbacks are
131 /// still running. Setting this to false will complete each packet
132 /// callback before the next packet is processed</param>
133 /// <remarks>This method will attempt to set the SIO_UDP_CONNRESET flag
134 /// on the socket to get newer versions of Windows to behave in a sane
135 /// manner (not throwing an exception when the remote side resets the
136 /// connection). This call is ignored on Mono where the flag is not
137 /// necessary</remarks>
138 public void StartInbound(int recvBufferSize, bool asyncPacketHandling)
139 {
140 m_asyncPacketHandling = asyncPacketHandling;
141  
142 if (!IsRunningInbound)
143 {
144 m_log.DebugFormat("[UDPBASE]: Starting inbound UDP loop");
145  
146 const int SIO_UDP_CONNRESET = -1744830452;
147  
148 IPEndPoint ipep = new IPEndPoint(m_localBindAddress, m_udpPort);
149  
150 m_log.DebugFormat(
151 "[UDPBASE]: Binding UDP listener using internal IP address config {0}:{1}",
152 ipep.Address, ipep.Port);
153  
154 m_udpSocket = new Socket(
155 AddressFamily.InterNetwork,
156 SocketType.Dgram,
157 ProtocolType.Udp);
158  
159 try
160 {
161 // This udp socket flag is not supported under mono,
162 // so we'll catch the exception and continue
163 m_udpSocket.IOControl(SIO_UDP_CONNRESET, new byte[] { 0 }, null);
164 m_log.Debug("[UDPBASE]: SIO_UDP_CONNRESET flag set");
165 }
166 catch (SocketException)
167 {
168 m_log.Debug("[UDPBASE]: SIO_UDP_CONNRESET flag not supported on this platform, ignoring");
169 }
170  
171 if (recvBufferSize != 0)
172 m_udpSocket.ReceiveBufferSize = recvBufferSize;
173  
174 m_udpSocket.Bind(ipep);
175  
176 IsRunningInbound = true;
177  
178 // kick off an async receive. The Start() method will return, the
179 // actual receives will occur asynchronously and will be caught in
180 // AsyncEndRecieve().
181 AsyncBeginReceive();
182 }
183 }
184  
185 /// <summary>
186 /// Start outbound UDP packet handling.
187 /// </summary>
188 public void StartOutbound()
189 {
190 m_log.DebugFormat("[UDPBASE]: Starting outbound UDP loop");
191  
192 IsRunningOutbound = true;
193 }
194  
195 public void StopInbound()
196 {
197 if (IsRunningInbound)
198 {
199 m_log.DebugFormat("[UDPBASE]: Stopping inbound UDP loop");
200  
201 IsRunningInbound = false;
202 m_udpSocket.Close();
203 }
204 }
205  
206 public void StopOutbound()
207 {
208 m_log.DebugFormat("[UDPBASE]: Stopping outbound UDP loop");
209  
210 IsRunningOutbound = false;
211 }
212  
213 protected virtual bool EnablePools()
214 {
215 if (!UsePools)
216 {
217 Pool = new Pool<UDPPacketBuffer>(() => new UDPPacketBuffer(), 500);
218  
219 UsePools = true;
220  
221 return true;
222 }
223  
224 return false;
225 }
226  
227 protected virtual bool DisablePools()
228 {
229 if (UsePools)
230 {
231 UsePools = false;
232  
233 // We won't null out the pool to avoid a race condition with code that may be in the middle of using it.
234  
235 return true;
236 }
237  
238 return false;
239 }
240  
241 private void AsyncBeginReceive()
242 {
243 UDPPacketBuffer buf;
244  
245 // FIXME: Disabled for now as this causes issues with reused packet objects interfering with each other
246 // on Windows with m_asyncPacketHandling = true, though this has not been seen on Linux.
247 // Possibly some unexpected issue with fetching UDP data concurrently with multiple threads. Requires more investigation.
248 // if (UsePools)
249 // buf = Pool.GetObject();
250 // else
251 buf = new UDPPacketBuffer();
252  
253 if (IsRunningInbound)
254 {
255 try
256 {
257 // kick off an async read
258 m_udpSocket.BeginReceiveFrom(
259 //wrappedBuffer.Instance.Data,
260 buf.Data,
261 0,
262 UDPPacketBuffer.BUFFER_SIZE,
263 SocketFlags.None,
264 ref buf.RemoteEndPoint,
265 AsyncEndReceive,
266 //wrappedBuffer);
267 buf);
268 }
269 catch (SocketException e)
270 {
271 if (e.SocketErrorCode == SocketError.ConnectionReset)
272 {
273 m_log.Warn("[UDPBASE]: SIO_UDP_CONNRESET was ignored, attempting to salvage the UDP listener on port " + m_udpPort);
274 bool salvaged = false;
275 while (!salvaged)
276 {
277 try
278 {
279 m_udpSocket.BeginReceiveFrom(
280 //wrappedBuffer.Instance.Data,
281 buf.Data,
282 0,
283 UDPPacketBuffer.BUFFER_SIZE,
284 SocketFlags.None,
285 ref buf.RemoteEndPoint,
286 AsyncEndReceive,
287 //wrappedBuffer);
288 buf);
289 salvaged = true;
290 }
291 catch (SocketException) { }
292 catch (ObjectDisposedException) { return; }
293 }
294  
295 m_log.Warn("[UDPBASE]: Salvaged the UDP listener on port " + m_udpPort);
296 }
297 }
298 catch (ObjectDisposedException e)
299 {
300 m_log.Error(
301 string.Format("[UDPBASE]: Error processing UDP begin receive {0}. Exception ", UdpReceives), e);
302 }
303 catch (Exception e)
304 {
305 m_log.Error(
306 string.Format("[UDPBASE]: Error processing UDP begin receive {0}. Exception ", UdpReceives), e);
307 }
308 }
309 }
310  
311 private void AsyncEndReceive(IAsyncResult iar)
312 {
313 // Asynchronous receive operations will complete here through the call
314 // to AsyncBeginReceive
315 if (IsRunningInbound)
316 {
317 UdpReceives++;
318  
319 // Asynchronous mode will start another receive before the
320 // callback for this packet is even fired. Very parallel :-)
321 if (m_asyncPacketHandling)
322 AsyncBeginReceive();
323  
324 try
325 {
326 // get the buffer that was created in AsyncBeginReceive
327 // this is the received data
328 UDPPacketBuffer buffer = (UDPPacketBuffer)iar.AsyncState;
329  
330 int startTick = Util.EnvironmentTickCount();
331  
332 // get the length of data actually read from the socket, store it with the
333 // buffer
334 buffer.DataLength = m_udpSocket.EndReceiveFrom(iar, ref buffer.RemoteEndPoint);
335  
336 // call the abstract method PacketReceived(), passing the buffer that
337 // has just been filled from the socket read.
338 PacketReceived(buffer);
339  
340 // If more than one thread can be calling AsyncEndReceive() at once (e.g. if m_asyncPacketHandler)
341 // then a particular stat may be inaccurate due to a race condition. We won't worry about this
342 // since this should be rare and won't cause a runtime problem.
343 if (m_currentReceiveTimeSamples >= s_receiveTimeSamples)
344 {
345 AverageReceiveTicksForLastSamplePeriod
346 = (float)m_receiveTicksInCurrentSamplePeriod / s_receiveTimeSamples;
347  
348 m_receiveTicksInCurrentSamplePeriod = 0;
349 m_currentReceiveTimeSamples = 0;
350 }
351 else
352 {
353 m_receiveTicksInCurrentSamplePeriod += Util.EnvironmentTickCountSubtract(startTick);
354 m_currentReceiveTimeSamples++;
355 }
356 }
357 catch (SocketException se)
358 {
359 m_log.Error(
360 string.Format(
361 "[UDPBASE]: Error processing UDP end receive {0}, socket error code {1}. Exception ",
362 UdpReceives, se.ErrorCode),
363 se);
364 }
365 catch (ObjectDisposedException e)
366 {
367 m_log.Error(
368 string.Format("[UDPBASE]: Error processing UDP end receive {0}. Exception ", UdpReceives), e);
369 }
370 catch (Exception e)
371 {
372 m_log.Error(
373 string.Format("[UDPBASE]: Error processing UDP end receive {0}. Exception ", UdpReceives), e);
374 }
375 finally
376 {
377 // if (UsePools)
378 // Pool.ReturnObject(buffer);
379  
380 // Synchronous mode waits until the packet callback completes
381 // before starting the receive to fetch another packet
382 if (!m_asyncPacketHandling)
383 AsyncBeginReceive();
384 }
385 }
386 }
387  
388 public void AsyncBeginSend(UDPPacketBuffer buf)
389 {
390 // if (IsRunningOutbound)
391 // {
392 try
393 {
394 m_udpSocket.BeginSendTo(
395 buf.Data,
396 0,
397 buf.DataLength,
398 SocketFlags.None,
399 buf.RemoteEndPoint,
400 AsyncEndSend,
401 buf);
402 }
403 catch (SocketException) { }
404 catch (ObjectDisposedException) { }
405 // }
406 }
407  
408 void AsyncEndSend(IAsyncResult result)
409 {
410 try
411 {
412 // UDPPacketBuffer buf = (UDPPacketBuffer)result.AsyncState;
413 m_udpSocket.EndSendTo(result);
414  
415 UdpSends++;
416 }
417 catch (SocketException) { }
418 catch (ObjectDisposedException) { }
419 }
420 }
421 }