From d8ee0cbe1cf93ca521f52ce39aa2a15cb5784e48 Mon Sep 17 00:00:00 2001 From: Diva Canto Date: Sat, 30 Apr 2011 09:24:15 -0700 Subject: First stab at cleaning up Caps. Compiles. Untested. --- .../ClientStack/Linden/UDP/OpenSimUDPBase.cs | 284 +++++++++++++++++++++ 1 file changed, 284 insertions(+) create mode 100644 OpenSim/Region/ClientStack/Linden/UDP/OpenSimUDPBase.cs (limited to 'OpenSim/Region/ClientStack/Linden/UDP/OpenSimUDPBase.cs') diff --git a/OpenSim/Region/ClientStack/Linden/UDP/OpenSimUDPBase.cs b/OpenSim/Region/ClientStack/Linden/UDP/OpenSimUDPBase.cs new file mode 100644 index 0000000..6eebd9d --- /dev/null +++ b/OpenSim/Region/ClientStack/Linden/UDP/OpenSimUDPBase.cs @@ -0,0 +1,284 @@ +/* + * Copyright (c) 2006, Clutch, Inc. + * Original Author: Jeff Cesnik + * All rights reserved. + * + * - Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * - Neither the name of the openmetaverse.org nor the names + * of its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.Net; +using System.Net.Sockets; +using System.Threading; +using log4net; + +namespace OpenMetaverse +{ + /// + /// Base UDP server + /// + public abstract class OpenSimUDPBase + { + private static readonly ILog m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + /// + /// This method is called when an incoming packet is received + /// + /// Incoming packet buffer + protected abstract void PacketReceived(UDPPacketBuffer buffer); + + /// UDP port to bind to in server mode + protected int m_udpPort; + + /// Local IP address to bind to in server mode + protected IPAddress m_localBindAddress; + + /// UDP socket, used in either client or server mode + private Socket m_udpSocket; + + /// Flag to process packets asynchronously or synchronously + private bool m_asyncPacketHandling; + + /// The all important shutdown flag + private volatile bool m_shutdownFlag = true; + + /// Returns true if the server is currently listening, otherwise false + public bool IsRunning { get { return !m_shutdownFlag; } } + + /// + /// Default constructor + /// + /// Local IP address to bind the server to + /// Port to listening for incoming UDP packets on + public OpenSimUDPBase(IPAddress bindAddress, int port) + { + m_localBindAddress = bindAddress; + m_udpPort = port; + } + + /// + /// Start the UDP server + /// + /// The size of the receive buffer for + /// the UDP socket. This value is passed up to the operating system + /// and used in the system networking stack. Use zero to leave this + /// value as the default + /// Set this to true to start + /// receiving more packets while current packet handler callbacks are + /// still running. Setting this to false will complete each packet + /// callback before the next packet is processed + /// This method will attempt to set the SIO_UDP_CONNRESET flag + /// on the socket to get newer versions of Windows to behave in a sane + /// manner (not throwing an exception when the remote side resets the + /// connection). This call is ignored on Mono where the flag is not + /// necessary + public void Start(int recvBufferSize, bool asyncPacketHandling) + { + m_asyncPacketHandling = asyncPacketHandling; + + if (m_shutdownFlag) + { + const int SIO_UDP_CONNRESET = -1744830452; + + IPEndPoint ipep = new IPEndPoint(m_localBindAddress, m_udpPort); + + m_log.DebugFormat( + "[UDPBASE]: Binding UDP listener using internal IP address config {0}:{1}", + ipep.Address, ipep.Port); + + m_udpSocket = new Socket( + AddressFamily.InterNetwork, + SocketType.Dgram, + ProtocolType.Udp); + + try + { + // This udp socket flag is not supported under mono, + // so we'll catch the exception and continue + m_udpSocket.IOControl(SIO_UDP_CONNRESET, new byte[] { 0 }, null); + m_log.Debug("[UDPBASE]: SIO_UDP_CONNRESET flag set"); + } + catch (SocketException) + { + m_log.Debug("[UDPBASE]: SIO_UDP_CONNRESET flag not supported on this platform, ignoring"); + } + + if (recvBufferSize != 0) + m_udpSocket.ReceiveBufferSize = recvBufferSize; + + m_udpSocket.Bind(ipep); + + // we're not shutting down, we're starting up + m_shutdownFlag = false; + + // kick off an async receive. The Start() method will return, the + // actual receives will occur asynchronously and will be caught in + // AsyncEndRecieve(). + AsyncBeginReceive(); + } + } + + /// + /// Stops the UDP server + /// + public void Stop() + { + if (!m_shutdownFlag) + { + // wait indefinitely for a writer lock. Once this is called, the .NET runtime + // will deny any more reader locks, in effect blocking all other send/receive + // threads. Once we have the lock, we set shutdownFlag to inform the other + // threads that the socket is closed. + m_shutdownFlag = true; + m_udpSocket.Close(); + } + } + + private void AsyncBeginReceive() + { + // allocate a packet buffer + //WrappedObject wrappedBuffer = Pool.CheckOut(); + UDPPacketBuffer buf = new UDPPacketBuffer(); + + if (!m_shutdownFlag) + { + try + { + // kick off an async read + m_udpSocket.BeginReceiveFrom( + //wrappedBuffer.Instance.Data, + buf.Data, + 0, + UDPPacketBuffer.BUFFER_SIZE, + SocketFlags.None, + ref buf.RemoteEndPoint, + AsyncEndReceive, + //wrappedBuffer); + buf); + } + catch (SocketException e) + { + if (e.SocketErrorCode == SocketError.ConnectionReset) + { + m_log.Warn("[UDPBASE]: SIO_UDP_CONNRESET was ignored, attempting to salvage the UDP listener on port " + m_udpPort); + bool salvaged = false; + while (!salvaged) + { + try + { + m_udpSocket.BeginReceiveFrom( + //wrappedBuffer.Instance.Data, + buf.Data, + 0, + UDPPacketBuffer.BUFFER_SIZE, + SocketFlags.None, + ref buf.RemoteEndPoint, + AsyncEndReceive, + //wrappedBuffer); + buf); + salvaged = true; + } + catch (SocketException) { } + catch (ObjectDisposedException) { return; } + } + + m_log.Warn("[UDPBASE]: Salvaged the UDP listener on port " + m_udpPort); + } + } + catch (ObjectDisposedException) { } + } + } + + private void AsyncEndReceive(IAsyncResult iar) + { + // Asynchronous receive operations will complete here through the call + // to AsyncBeginReceive + if (!m_shutdownFlag) + { + // Asynchronous mode will start another receive before the + // callback for this packet is even fired. Very parallel :-) + if (m_asyncPacketHandling) + AsyncBeginReceive(); + + // get the buffer that was created in AsyncBeginReceive + // this is the received data + //WrappedObject wrappedBuffer = (WrappedObject)iar.AsyncState; + //UDPPacketBuffer buffer = wrappedBuffer.Instance; + UDPPacketBuffer buffer = (UDPPacketBuffer)iar.AsyncState; + + try + { + // get the length of data actually read from the socket, store it with the + // buffer + buffer.DataLength = m_udpSocket.EndReceiveFrom(iar, ref buffer.RemoteEndPoint); + + // call the abstract method PacketReceived(), passing the buffer that + // has just been filled from the socket read. + PacketReceived(buffer); + } + catch (SocketException) { } + catch (ObjectDisposedException) { } + finally + { + //wrappedBuffer.Dispose(); + + // Synchronous mode waits until the packet callback completes + // before starting the receive to fetch another packet + if (!m_asyncPacketHandling) + AsyncBeginReceive(); + } + + } + } + + public void AsyncBeginSend(UDPPacketBuffer buf) + { + if (!m_shutdownFlag) + { + try + { + m_udpSocket.BeginSendTo( + buf.Data, + 0, + buf.DataLength, + SocketFlags.None, + buf.RemoteEndPoint, + AsyncEndSend, + buf); + } + catch (SocketException) { } + catch (ObjectDisposedException) { } + } + } + + void AsyncEndSend(IAsyncResult result) + { + try + { +// UDPPacketBuffer buf = (UDPPacketBuffer)result.AsyncState; + m_udpSocket.EndSendTo(result); + } + catch (SocketException) { } + catch (ObjectDisposedException) { } + } + } +} -- cgit v1.1