aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/ClientStack/Linden/UDP/OpenSimUDPBase.cs
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Region/ClientStack/Linden/UDP/OpenSimUDPBase.cs')
-rw-r--r--OpenSim/Region/ClientStack/Linden/UDP/OpenSimUDPBase.cs284
1 files changed, 284 insertions, 0 deletions
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 @@
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
28using System;
29using System.Net;
30using System.Net.Sockets;
31using System.Threading;
32using log4net;
33
34namespace OpenMetaverse
35{
36 /// <summary>
37 /// Base UDP server
38 /// </summary>
39 public abstract class OpenSimUDPBase
40 {
41 private static readonly ILog m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
42
43 /// <summary>
44 /// This method is called when an incoming packet is received
45 /// </summary>
46 /// <param name="buffer">Incoming packet buffer</param>
47 protected abstract void PacketReceived(UDPPacketBuffer buffer);
48
49 /// <summary>UDP port to bind to in server mode</summary>
50 protected int m_udpPort;
51
52 /// <summary>Local IP address to bind to in server mode</summary>
53 protected IPAddress m_localBindAddress;
54
55 /// <summary>UDP socket, used in either client or server mode</summary>
56 private Socket m_udpSocket;
57
58 /// <summary>Flag to process packets asynchronously or synchronously</summary>
59 private bool m_asyncPacketHandling;
60
61 /// <summary>The all important shutdown flag</summary>
62 private volatile bool m_shutdownFlag = true;
63
64 /// <summary>Returns true if the server is currently listening, otherwise false</summary>
65 public bool IsRunning { get { return !m_shutdownFlag; } }
66
67 /// <summary>
68 /// Default constructor
69 /// </summary>
70 /// <param name="bindAddress">Local IP address to bind the server to</param>
71 /// <param name="port">Port to listening for incoming UDP packets on</param>
72 public OpenSimUDPBase(IPAddress bindAddress, int port)
73 {
74 m_localBindAddress = bindAddress;
75 m_udpPort = port;
76 }
77
78 /// <summary>
79 /// Start the UDP server
80 /// </summary>
81 /// <param name="recvBufferSize">The size of the receive buffer for
82 /// the UDP socket. This value is passed up to the operating system
83 /// and used in the system networking stack. Use zero to leave this
84 /// value as the default</param>
85 /// <param name="asyncPacketHandling">Set this to true to start
86 /// receiving more packets while current packet handler callbacks are
87 /// still running. Setting this to false will complete each packet
88 /// callback before the next packet is processed</param>
89 /// <remarks>This method will attempt to set the SIO_UDP_CONNRESET flag
90 /// on the socket to get newer versions of Windows to behave in a sane
91 /// manner (not throwing an exception when the remote side resets the
92 /// connection). This call is ignored on Mono where the flag is not
93 /// necessary</remarks>
94 public void Start(int recvBufferSize, bool asyncPacketHandling)
95 {
96 m_asyncPacketHandling = asyncPacketHandling;
97
98 if (m_shutdownFlag)
99 {
100 const int SIO_UDP_CONNRESET = -1744830452;
101
102 IPEndPoint ipep = new IPEndPoint(m_localBindAddress, m_udpPort);
103
104 m_log.DebugFormat(
105 "[UDPBASE]: Binding UDP listener using internal IP address config {0}:{1}",
106 ipep.Address, ipep.Port);
107
108 m_udpSocket = new Socket(
109 AddressFamily.InterNetwork,
110 SocketType.Dgram,
111 ProtocolType.Udp);
112
113 try
114 {
115 // This udp socket flag is not supported under mono,
116 // so we'll catch the exception and continue
117 m_udpSocket.IOControl(SIO_UDP_CONNRESET, new byte[] { 0 }, null);
118 m_log.Debug("[UDPBASE]: SIO_UDP_CONNRESET flag set");
119 }
120 catch (SocketException)
121 {
122 m_log.Debug("[UDPBASE]: SIO_UDP_CONNRESET flag not supported on this platform, ignoring");
123 }
124
125 if (recvBufferSize != 0)
126 m_udpSocket.ReceiveBufferSize = recvBufferSize;
127
128 m_udpSocket.Bind(ipep);
129
130 // we're not shutting down, we're starting up
131 m_shutdownFlag = false;
132
133 // kick off an async receive. The Start() method will return, the
134 // actual receives will occur asynchronously and will be caught in
135 // AsyncEndRecieve().
136 AsyncBeginReceive();
137 }
138 }
139
140 /// <summary>
141 /// Stops the UDP server
142 /// </summary>
143 public void Stop()
144 {
145 if (!m_shutdownFlag)
146 {
147 // wait indefinitely for a writer lock. Once this is called, the .NET runtime
148 // will deny any more reader locks, in effect blocking all other send/receive
149 // threads. Once we have the lock, we set shutdownFlag to inform the other
150 // threads that the socket is closed.
151 m_shutdownFlag = true;
152 m_udpSocket.Close();
153 }
154 }
155
156 private void AsyncBeginReceive()
157 {
158 // allocate a packet buffer
159 //WrappedObject<UDPPacketBuffer> wrappedBuffer = Pool.CheckOut();
160 UDPPacketBuffer buf = new UDPPacketBuffer();
161
162 if (!m_shutdownFlag)
163 {
164 try
165 {
166 // kick off an async read
167 m_udpSocket.BeginReceiveFrom(
168 //wrappedBuffer.Instance.Data,
169 buf.Data,
170 0,
171 UDPPacketBuffer.BUFFER_SIZE,
172 SocketFlags.None,
173 ref buf.RemoteEndPoint,
174 AsyncEndReceive,
175 //wrappedBuffer);
176 buf);
177 }
178 catch (SocketException e)
179 {
180 if (e.SocketErrorCode == SocketError.ConnectionReset)
181 {
182 m_log.Warn("[UDPBASE]: SIO_UDP_CONNRESET was ignored, attempting to salvage the UDP listener on port " + m_udpPort);
183 bool salvaged = false;
184 while (!salvaged)
185 {
186 try
187 {
188 m_udpSocket.BeginReceiveFrom(
189 //wrappedBuffer.Instance.Data,
190 buf.Data,
191 0,
192 UDPPacketBuffer.BUFFER_SIZE,
193 SocketFlags.None,
194 ref buf.RemoteEndPoint,
195 AsyncEndReceive,
196 //wrappedBuffer);
197 buf);
198 salvaged = true;
199 }
200 catch (SocketException) { }
201 catch (ObjectDisposedException) { return; }
202 }
203
204 m_log.Warn("[UDPBASE]: Salvaged the UDP listener on port " + m_udpPort);
205 }
206 }
207 catch (ObjectDisposedException) { }
208 }
209 }
210
211 private void AsyncEndReceive(IAsyncResult iar)
212 {
213 // Asynchronous receive operations will complete here through the call
214 // to AsyncBeginReceive
215 if (!m_shutdownFlag)
216 {
217 // Asynchronous mode will start another receive before the
218 // callback for this packet is even fired. Very parallel :-)
219 if (m_asyncPacketHandling)
220 AsyncBeginReceive();
221
222 // get the buffer that was created in AsyncBeginReceive
223 // this is the received data
224 //WrappedObject<UDPPacketBuffer> wrappedBuffer = (WrappedObject<UDPPacketBuffer>)iar.AsyncState;
225 //UDPPacketBuffer buffer = wrappedBuffer.Instance;
226 UDPPacketBuffer buffer = (UDPPacketBuffer)iar.AsyncState;
227
228 try
229 {
230 // get the length of data actually read from the socket, store it with the
231 // buffer
232 buffer.DataLength = m_udpSocket.EndReceiveFrom(iar, ref buffer.RemoteEndPoint);
233
234 // call the abstract method PacketReceived(), passing the buffer that
235 // has just been filled from the socket read.
236 PacketReceived(buffer);
237 }
238 catch (SocketException) { }
239 catch (ObjectDisposedException) { }
240 finally
241 {
242 //wrappedBuffer.Dispose();
243
244 // Synchronous mode waits until the packet callback completes
245 // before starting the receive to fetch another packet
246 if (!m_asyncPacketHandling)
247 AsyncBeginReceive();
248 }
249
250 }
251 }
252
253 public void AsyncBeginSend(UDPPacketBuffer buf)
254 {
255 if (!m_shutdownFlag)
256 {
257 try
258 {
259 m_udpSocket.BeginSendTo(
260 buf.Data,
261 0,
262 buf.DataLength,
263 SocketFlags.None,
264 buf.RemoteEndPoint,
265 AsyncEndSend,
266 buf);
267 }
268 catch (SocketException) { }
269 catch (ObjectDisposedException) { }
270 }
271 }
272
273 void AsyncEndSend(IAsyncResult result)
274 {
275 try
276 {
277// UDPPacketBuffer buf = (UDPPacketBuffer)result.AsyncState;
278 m_udpSocket.EndSendTo(result);
279 }
280 catch (SocketException) { }
281 catch (ObjectDisposedException) { }
282 }
283 }
284}