diff options
Diffstat (limited to 'OpenSim/Region/ClientStack/Linden/UDP/OpenSimUDPBase.cs')
-rw-r--r-- | OpenSim/Region/ClientStack/Linden/UDP/OpenSimUDPBase.cs | 280 |
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 | |||
28 | using System; | ||
29 | using System.Net; | ||
30 | using System.Net.Sockets; | ||
31 | using System.Threading; | ||
32 | using log4net; | ||
33 | |||
34 | namespace 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 | } | ||