diff options
author | Melanie | 2009-10-08 22:25:14 +0100 |
---|---|---|
committer | Melanie | 2009-10-08 22:25:14 +0100 |
commit | a0dd9f4bb4c1a88c2692066c54923c7f91429df5 (patch) | |
tree | 700b656c3662b62d73fe2c13894d1c57fab25197 /OpenSim/Region/ClientStack/LindenUDP/OpenSimUDPBase.cs | |
parent | Added guards against the exception reported: (diff) | |
download | opensim-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.cs | 340 |
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 | |||
28 | using System; | ||
29 | using System.Net; | ||
30 | using System.Net.Sockets; | ||
31 | using System.Threading; | ||
32 | using OpenMetaverse; | ||
33 | |||
34 | namespace 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 | } | ||