aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/ClientStack/LindenUDP/OpenSimUDPBase.cs
diff options
context:
space:
mode:
authorMelanie2009-10-08 22:25:14 +0100
committerMelanie2009-10-08 22:25:14 +0100
commita0dd9f4bb4c1a88c2692066c54923c7f91429df5 (patch)
tree700b656c3662b62d73fe2c13894d1c57fab25197 /OpenSim/Region/ClientStack/LindenUDP/OpenSimUDPBase.cs
parentAdded guards against the exception reported: (diff)
downloadopensim-SC_OLD-a0dd9f4bb4c1a88c2692066c54923c7f91429df5.zip
opensim-SC_OLD-a0dd9f4bb4c1a88c2692066c54923c7f91429df5.tar.gz
opensim-SC_OLD-a0dd9f4bb4c1a88c2692066c54923c7f91429df5.tar.bz2
opensim-SC_OLD-a0dd9f4bb4c1a88c2692066c54923c7f91429df5.tar.xz
Fork UDPBase from libOMV into opensim
Diffstat (limited to 'OpenSim/Region/ClientStack/LindenUDP/OpenSimUDPBase.cs')
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/OpenSimUDPBase.cs340
1 files changed, 340 insertions, 0 deletions
diff --git a/OpenSim/Region/ClientStack/LindenUDP/OpenSimUDPBase.cs b/OpenSim/Region/ClientStack/LindenUDP/OpenSimUDPBase.cs
new file mode 100644
index 0000000..aaefbc6
--- /dev/null
+++ b/OpenSim/Region/ClientStack/LindenUDP/OpenSimUDPBase.cs
@@ -0,0 +1,340 @@
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 OpenMetaverse;
33
34namespace OpenSim.Region.ClientStack.LindenUDP
35{
36 /// <summary>
37 ///
38 /// </summary>
39 public abstract class OpenSimUDPBase
40 {
41 // these abstract methods must be implemented in a derived class to actually do
42 // something with the packets that are sent and received.
43 protected abstract void PacketReceived(UDPPacketBuffer buffer);
44 protected abstract void PacketSent(UDPPacketBuffer buffer, int bytesSent);
45
46 // the port to listen on
47 internal int udpPort;
48
49 // the UDP socket
50 private Socket udpSocket;
51
52 // the ReaderWriterLock is used solely for the purposes of shutdown (Stop()).
53 // since there are potentially many "reader" threads in the internal .NET IOCP
54 // thread pool, this is a cheaper synchronization primitive than using
55 // a Mutex object. This allows many UDP socket "reads" concurrently - when
56 // Stop() is called, it attempts to obtain a writer lock which will then
57 // wait until all outstanding operations are completed before shutting down.
58 // this avoids the problem of closing the socket with outstanding operations
59 // and trying to catch the inevitable ObjectDisposedException.
60 private ReaderWriterLock rwLock = new ReaderWriterLock();
61
62 // number of outstanding operations. This is a reference count
63 // which we use to ensure that the threads exit cleanly. Note that
64 // we need this because the threads will potentially still need to process
65 // data even after the socket is closed.
66 private int rwOperationCount = 0;
67
68 // the all important shutdownFlag. This is synchronized through the ReaderWriterLock.
69 private volatile bool shutdownFlag = true;
70
71 // the remote endpoint to communicate with
72 protected IPEndPoint remoteEndPoint = null;
73
74
75 /// <summary>
76 /// Initialize the UDP packet handler in server mode
77 /// </summary>
78 /// <param name="port">Port to listening for incoming UDP packets on</param>
79 public OpenSimUDPBase(int port)
80 {
81 udpPort = port;
82 }
83
84 /// <summary>
85 /// Initialize the UDP packet handler in client mode
86 /// </summary>
87 /// <param name="endPoint">Remote UDP server to connect to</param>
88 public OpenSimUDPBase(IPEndPoint endPoint)
89 {
90 remoteEndPoint = endPoint;
91 udpPort = 0;
92 }
93
94 /// <summary>
95 ///
96 /// </summary>
97 public void Start()
98 {
99 if (shutdownFlag)
100 {
101 if (remoteEndPoint == null)
102 {
103 // Server mode
104
105 // create and bind the socket
106 IPEndPoint ipep = new IPEndPoint(Settings.BIND_ADDR, udpPort);
107 udpSocket = new Socket(
108 AddressFamily.InterNetwork,
109 SocketType.Dgram,
110 ProtocolType.Udp);
111 udpSocket.Bind(ipep);
112 }
113 else
114 {
115 // Client mode
116 IPEndPoint ipep = new IPEndPoint(Settings.BIND_ADDR, udpPort);
117 udpSocket = new Socket(
118 AddressFamily.InterNetwork,
119 SocketType.Dgram,
120 ProtocolType.Udp);
121 udpSocket.Bind(ipep);
122 //udpSocket.Connect(remoteEndPoint);
123 }
124
125 // we're not shutting down, we're starting up
126 shutdownFlag = false;
127
128 // kick off an async receive. The Start() method will return, the
129 // actual receives will occur asynchronously and will be caught in
130 // AsyncEndRecieve().
131 AsyncBeginReceive();
132 }
133 }
134
135 /// <summary>
136 ///
137 /// </summary>
138 public void Stop()
139 {
140 if (!shutdownFlag)
141 {
142 // wait indefinitely for a writer lock. Once this is called, the .NET runtime
143 // will deny any more reader locks, in effect blocking all other send/receive
144 // threads. Once we have the lock, we set shutdownFlag to inform the other
145 // threads that the socket is closed.
146 rwLock.AcquireWriterLock(-1);
147 shutdownFlag = true;
148 udpSocket.Close();
149 rwLock.ReleaseWriterLock();
150
151 // wait for any pending operations to complete on other
152 // threads before exiting.
153 const int FORCE_STOP = 100;
154 int i = 0;
155 while (rwOperationCount > 0 && i < FORCE_STOP)
156 {
157 Thread.Sleep(10);
158 ++i;
159 }
160
161 if (i >= FORCE_STOP)
162 {
163 Logger.Log("UDPBase.Stop() forced shutdown while waiting on pending operations",
164 Helpers.LogLevel.Warning);
165 }
166 }
167 }
168
169 /// <summary>
170 ///
171 /// </summary>
172 public bool IsRunning
173 {
174 get { return !shutdownFlag; }
175 }
176
177 private void AsyncBeginReceive()
178 {
179 // this method actually kicks off the async read on the socket.
180 // we aquire a reader lock here to ensure that no other thread
181 // is trying to set shutdownFlag and close the socket.
182 rwLock.AcquireReaderLock(-1);
183
184 if (!shutdownFlag)
185 {
186 // increment the count of pending operations
187 Interlocked.Increment(ref rwOperationCount);
188
189 // allocate a packet buffer
190 //WrappedObject<UDPPacketBuffer> wrappedBuffer = Pool.CheckOut();
191 UDPPacketBuffer buf = new UDPPacketBuffer();
192
193 try
194 {
195 // kick off an async read
196 udpSocket.BeginReceiveFrom(
197 //wrappedBuffer.Instance.Data,
198 buf.Data,
199 0,
200 UDPPacketBuffer.BUFFER_SIZE,
201 SocketFlags.None,
202 //ref wrappedBuffer.Instance.RemoteEndPoint,
203 ref buf.RemoteEndPoint,
204 new AsyncCallback(AsyncEndReceive),
205 //wrappedBuffer);
206 buf);
207 }
208 catch (SocketException)
209 {
210 // something bad happened
211 //Logger.Log(
212 // "A SocketException occurred in UDPServer.AsyncBeginReceive()",
213 // Helpers.LogLevel.Error, se);
214
215 // an error occurred, therefore the operation is void. Decrement the reference count.
216 Interlocked.Decrement(ref rwOperationCount);
217 }
218 }
219
220 // we're done with the socket for now, release the reader lock.
221 rwLock.ReleaseReaderLock();
222 }
223
224 private void AsyncEndReceive(IAsyncResult iar)
225 {
226 // Asynchronous receive operations will complete here through the call
227 // to AsyncBeginReceive
228
229 // aquire a reader lock
230 rwLock.AcquireReaderLock(-1);
231
232 if (!shutdownFlag)
233 {
234 // get the buffer that was created in AsyncBeginReceive
235 // this is the received data
236 //WrappedObject<UDPPacketBuffer> wrappedBuffer = (WrappedObject<UDPPacketBuffer>)iar.AsyncState;
237 //UDPPacketBuffer buffer = wrappedBuffer.Instance;
238 UDPPacketBuffer buffer = (UDPPacketBuffer)iar.AsyncState;
239
240 try
241 {
242 // get the length of data actually read from the socket, store it with the
243 // buffer
244 buffer.DataLength = udpSocket.EndReceiveFrom(iar, ref buffer.RemoteEndPoint);
245
246 // this operation is now complete, decrement the reference count
247 Interlocked.Decrement(ref rwOperationCount);
248
249 // we're done with the socket, release the reader lock
250 rwLock.ReleaseReaderLock();
251
252 // call the abstract method PacketReceived(), passing the buffer that
253 // has just been filled from the socket read.
254 PacketReceived(buffer);
255 }
256 catch (SocketException)
257 {
258 // an error occurred, therefore the operation is void. Decrement the reference count.
259 Interlocked.Decrement(ref rwOperationCount);
260
261 // we're done with the socket for now, release the reader lock.
262 rwLock.ReleaseReaderLock();
263 }
264 finally
265 {
266 // start another receive - this keeps the server going!
267 AsyncBeginReceive();
268
269 //wrappedBuffer.Dispose();
270 }
271 }
272 else
273 {
274 // nothing bad happened, but we are done with the operation
275 // decrement the reference count and release the reader lock
276 Interlocked.Decrement(ref rwOperationCount);
277 rwLock.ReleaseReaderLock();
278 }
279 }
280
281 public void AsyncBeginSend(UDPPacketBuffer buf)
282 {
283 rwLock.AcquireReaderLock(-1);
284
285 if (!shutdownFlag)
286 {
287 try
288 {
289 Interlocked.Increment(ref rwOperationCount);
290 udpSocket.BeginSendTo(
291 buf.Data,
292 0,
293 buf.DataLength,
294 SocketFlags.None,
295 buf.RemoteEndPoint,
296 new AsyncCallback(AsyncEndSend),
297 buf);
298 }
299 catch (SocketException)
300 {
301 //Logger.Log(
302 // "A SocketException occurred in UDPServer.AsyncBeginSend()",
303 // Helpers.LogLevel.Error, se);
304 }
305 }
306
307 rwLock.ReleaseReaderLock();
308 }
309
310 private void AsyncEndSend(IAsyncResult iar)
311 {
312 rwLock.AcquireReaderLock(-1);
313
314 if (!shutdownFlag)
315 {
316 UDPPacketBuffer buffer = (UDPPacketBuffer)iar.AsyncState;
317
318 try
319 {
320 int bytesSent = udpSocket.EndSendTo(iar);
321
322 // note that call to the abstract PacketSent() method - we are passing the number
323 // of bytes sent in a separate parameter, since we can't use buffer.DataLength which
324 // is the number of bytes to send (or bytes received depending upon whether this
325 // buffer was part of a send or a receive).
326 PacketSent(buffer, bytesSent);
327 }
328 catch (SocketException)
329 {
330 //Logger.Log(
331 // "A SocketException occurred in UDPServer.AsyncEndSend()",
332 // Helpers.LogLevel.Error, se);
333 }
334 }
335
336 Interlocked.Decrement(ref rwOperationCount);
337 rwLock.ReleaseReaderLock();
338 }
339 }
340}