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.cs280
1 files changed, 280 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..d2779ba
--- /dev/null
+++ b/OpenSim/Region/ClientStack/Linden/UDP/OpenSimUDPBase.cs
@@ -0,0 +1,280 @@
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_udpSocket = new Socket(
105 AddressFamily.InterNetwork,
106 SocketType.Dgram,
107 ProtocolType.Udp);
108
109 try
110 {
111 // This udp socket flag is not supported under mono,
112 // so we'll catch the exception and continue
113 m_udpSocket.IOControl(SIO_UDP_CONNRESET, new byte[] { 0 }, null);
114 m_log.Debug("[UDPBASE]: SIO_UDP_CONNRESET flag set");
115 }
116 catch (SocketException)
117 {
118 m_log.Debug("[UDPBASE]: SIO_UDP_CONNRESET flag not supported on this platform, ignoring");
119 }
120
121 if (recvBufferSize != 0)
122 m_udpSocket.ReceiveBufferSize = recvBufferSize;
123
124 m_udpSocket.Bind(ipep);
125
126 // we're not shutting down, we're starting up
127 m_shutdownFlag = false;
128
129 // kick off an async receive. The Start() method will return, the
130 // actual receives will occur asynchronously and will be caught in
131 // AsyncEndRecieve().
132 AsyncBeginReceive();
133 }
134 }
135
136 /// <summary>
137 /// Stops the UDP server
138 /// </summary>
139 public void Stop()
140 {
141 if (!m_shutdownFlag)
142 {
143 // wait indefinitely for a writer lock. Once this is called, the .NET runtime
144 // will deny any more reader locks, in effect blocking all other send/receive
145 // threads. Once we have the lock, we set shutdownFlag to inform the other
146 // threads that the socket is closed.
147 m_shutdownFlag = true;
148 m_udpSocket.Close();
149 }
150 }
151
152 private void AsyncBeginReceive()
153 {
154 // allocate a packet buffer
155 //WrappedObject<UDPPacketBuffer> wrappedBuffer = Pool.CheckOut();
156 UDPPacketBuffer buf = new UDPPacketBuffer();
157
158 if (!m_shutdownFlag)
159 {
160 try
161 {
162 // kick off an async read
163 m_udpSocket.BeginReceiveFrom(
164 //wrappedBuffer.Instance.Data,
165 buf.Data,
166 0,
167 UDPPacketBuffer.BUFFER_SIZE,
168 SocketFlags.None,
169 ref buf.RemoteEndPoint,
170 AsyncEndReceive,
171 //wrappedBuffer);
172 buf);
173 }
174 catch (SocketException e)
175 {
176 if (e.SocketErrorCode == SocketError.ConnectionReset)
177 {
178 m_log.Warn("[UDPBASE]: SIO_UDP_CONNRESET was ignored, attempting to salvage the UDP listener on port " + m_udpPort);
179 bool salvaged = false;
180 while (!salvaged)
181 {
182 try
183 {
184 m_udpSocket.BeginReceiveFrom(
185 //wrappedBuffer.Instance.Data,
186 buf.Data,
187 0,
188 UDPPacketBuffer.BUFFER_SIZE,
189 SocketFlags.None,
190 ref buf.RemoteEndPoint,
191 AsyncEndReceive,
192 //wrappedBuffer);
193 buf);
194 salvaged = true;
195 }
196 catch (SocketException) { }
197 catch (ObjectDisposedException) { return; }
198 }
199
200 m_log.Warn("[UDPBASE]: Salvaged the UDP listener on port " + m_udpPort);
201 }
202 }
203 catch (ObjectDisposedException) { }
204 }
205 }
206
207 private void AsyncEndReceive(IAsyncResult iar)
208 {
209 // Asynchronous receive operations will complete here through the call
210 // to AsyncBeginReceive
211 if (!m_shutdownFlag)
212 {
213 // Asynchronous mode will start another receive before the
214 // callback for this packet is even fired. Very parallel :-)
215 if (m_asyncPacketHandling)
216 AsyncBeginReceive();
217
218 // get the buffer that was created in AsyncBeginReceive
219 // this is the received data
220 //WrappedObject<UDPPacketBuffer> wrappedBuffer = (WrappedObject<UDPPacketBuffer>)iar.AsyncState;
221 //UDPPacketBuffer buffer = wrappedBuffer.Instance;
222 UDPPacketBuffer buffer = (UDPPacketBuffer)iar.AsyncState;
223
224 try
225 {
226 // get the length of data actually read from the socket, store it with the
227 // buffer
228 buffer.DataLength = m_udpSocket.EndReceiveFrom(iar, ref buffer.RemoteEndPoint);
229
230 // call the abstract method PacketReceived(), passing the buffer that
231 // has just been filled from the socket read.
232 PacketReceived(buffer);
233 }
234 catch (SocketException) { }
235 catch (ObjectDisposedException) { }
236 finally
237 {
238 //wrappedBuffer.Dispose();
239
240 // Synchronous mode waits until the packet callback completes
241 // before starting the receive to fetch another packet
242 if (!m_asyncPacketHandling)
243 AsyncBeginReceive();
244 }
245
246 }
247 }
248
249 public void AsyncBeginSend(UDPPacketBuffer buf)
250 {
251 if (!m_shutdownFlag)
252 {
253 try
254 {
255 m_udpSocket.BeginSendTo(
256 buf.Data,
257 0,
258 buf.DataLength,
259 SocketFlags.None,
260 buf.RemoteEndPoint,
261 AsyncEndSend,
262 buf);
263 }
264 catch (SocketException) { }
265 catch (ObjectDisposedException) { }
266 }
267 }
268
269 void AsyncEndSend(IAsyncResult result)
270 {
271 try
272 {
273// UDPPacketBuffer buf = (UDPPacketBuffer)result.AsyncState;
274 m_udpSocket.EndSendTo(result);
275 }
276 catch (SocketException) { }
277 catch (ObjectDisposedException) { }
278 }
279 }
280}