aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/ClientStack/LindenUDP/OpenSimUDPBase.cs
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Region/ClientStack/LindenUDP/OpenSimUDPBase.cs')
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/OpenSimUDPBase.cs297
1 files changed, 110 insertions, 187 deletions
diff --git a/OpenSim/Region/ClientStack/LindenUDP/OpenSimUDPBase.cs b/OpenSim/Region/ClientStack/LindenUDP/OpenSimUDPBase.cs
index 218aaac..e78a4fe 100644
--- a/OpenSim/Region/ClientStack/LindenUDP/OpenSimUDPBase.cs
+++ b/OpenSim/Region/ClientStack/LindenUDP/OpenSimUDPBase.cs
@@ -29,101 +29,90 @@ using System;
29using System.Net; 29using System.Net;
30using System.Net.Sockets; 30using System.Net.Sockets;
31using System.Threading; 31using System.Threading;
32using OpenMetaverse; 32using log4net;
33 33
34namespace OpenSim.Region.ClientStack.LindenUDP 34namespace OpenMetaverse
35{ 35{
36 /// <summary> 36 /// <summary>
37 /// 37 /// Base UDP server
38 /// </summary> 38 /// </summary>
39 public abstract class OpenSimUDPBase 39 public abstract class OpenSimUDPBase
40 { 40 {
41 // these abstract methods must be implemented in a derived class to actually do 41 private static readonly ILog m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
42 // something with the packets that are sent and received. 42
43 /// <summary>
44 /// This method is called when an incoming packet is received
45 /// </summary>
46 /// <param name="buffer">Incoming packet buffer</param>
43 protected abstract void PacketReceived(UDPPacketBuffer buffer); 47 protected abstract void PacketReceived(UDPPacketBuffer buffer);
48
49 /// <summary>
50 /// This method is called when an outgoing packet is sent
51 /// </summary>
52 /// <param name="buffer">Outgoing packet buffer</param>
53 /// <param name="bytesSent">Number of bytes written to the wire</param>
44 protected abstract void PacketSent(UDPPacketBuffer buffer, int bytesSent); 54 protected abstract void PacketSent(UDPPacketBuffer buffer, int bytesSent);
45 55
46 // the port to listen on 56 /// <summary>UDP port to bind to in server mode</summary>
47 internal int udpPort; 57 protected int m_udpPort;
48
49 // the UDP socket
50 private Socket udpSocket;
51 58
52 // the ReaderWriterLock is used solely for the purposes of shutdown (Stop()). 59 /// <summary>Local IP address to bind to in server mode</summary>
53 // since there are potentially many "reader" threads in the internal .NET IOCP 60 protected IPAddress m_localBindAddress;
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 61
62 // number of outstanding operations. This is a reference count 62 /// <summary>UDP socket, used in either client or server mode</summary>
63 // which we use to ensure that the threads exit cleanly. Note that 63 private Socket m_udpSocket;
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 64
68 // the all important shutdownFlag. This is synchronized through the ReaderWriterLock. 65 /// <summary>The all important shutdown flag</summary>
69 private volatile bool shutdownFlag = true; 66 private volatile bool m_shutdownFlag = true;
70
71 // the remote endpoint to communicate with
72 protected IPEndPoint remoteEndPoint = null;
73 67
68 /// <summary>Returns true if the server is currently listening, otherwise false</summary>
69 public bool IsRunning { get { return !m_shutdownFlag; } }
74 70
75 /// <summary> 71 /// <summary>
76 /// Initialize the UDP packet handler in server mode 72 /// Default constructor
77 /// </summary> 73 /// </summary>
74 /// <param name="bindAddress">Local IP address to bind the server to</param>
78 /// <param name="port">Port to listening for incoming UDP packets on</param> 75 /// <param name="port">Port to listening for incoming UDP packets on</param>
79 public OpenSimUDPBase(int port) 76 public OpenSimUDPBase(IPAddress bindAddress, int port)
80 { 77 {
81 udpPort = port; 78 m_localBindAddress = bindAddress;
79 m_udpPort = port;
82 } 80 }
83 81
84 /// <summary> 82 /// <summary>
85 /// Initialize the UDP packet handler in client mode 83 /// Start the UDP server
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> 84 /// </summary>
85 /// <remarks>This method will attempt to set the SIO_UDP_CONNRESET flag
86 /// on the socket to get newer versions of Windows to behave in a sane
87 /// manner (not throwing an exception when the remote side resets the
88 /// connection). This call is ignored on Mono where the flag is not
89 /// necessary</remarks>
97 public void Start() 90 public void Start()
98 { 91 {
99 if (shutdownFlag) 92 if (m_shutdownFlag)
100 { 93 {
101 if (remoteEndPoint == null) 94 const int SIO_UDP_CONNRESET = -1744830452;
102 {
103 // Server mode
104 95
105 // create and bind the socket 96 IPEndPoint ipep = new IPEndPoint(m_localBindAddress, m_udpPort);
106 IPEndPoint ipep = new IPEndPoint(Settings.BIND_ADDR, udpPort); 97 m_udpSocket = new Socket(
107 udpSocket = new Socket( 98 AddressFamily.InterNetwork,
108 AddressFamily.InterNetwork, 99 SocketType.Dgram,
109 SocketType.Dgram, 100 ProtocolType.Udp);
110 ProtocolType.Udp); 101 try
111 udpSocket.Bind(ipep); 102 {
103 // this udp socket flag is not supported under mono,
104 // so we'll catch the exception and continue
105 m_udpSocket.IOControl(SIO_UDP_CONNRESET, new byte[] { 0 }, null);
106 m_log.Debug("[UDPBASE]: SIO_UDP_CONNRESET flag set");
112 } 107 }
113 else 108 catch (SocketException)
114 { 109 {
115 // Client mode 110 m_log.Debug("[UDPBASE]: SIO_UDP_CONNRESET flag not supported on this platform, ignoring");
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 } 111 }
112 m_udpSocket.Bind(ipep);
124 113
125 // we're not shutting down, we're starting up 114 // we're not shutting down, we're starting up
126 shutdownFlag = false; 115 m_shutdownFlag = false;
127 116
128 // kick off an async receive. The Start() method will return, the 117 // kick off an async receive. The Start() method will return, the
129 // actual receives will occur asynchronously and will be caught in 118 // actual receives will occur asynchronously and will be caught in
@@ -133,103 +122,81 @@ namespace OpenSim.Region.ClientStack.LindenUDP
133 } 122 }
134 123
135 /// <summary> 124 /// <summary>
136 /// 125 /// Stops the UDP server
137 /// </summary> 126 /// </summary>
138 public void Stop() 127 public void Stop()
139 { 128 {
140 if (!shutdownFlag) 129 if (!m_shutdownFlag)
141 { 130 {
142 // wait indefinitely for a writer lock. Once this is called, the .NET runtime 131 // 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 132 // 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 133 // threads. Once we have the lock, we set shutdownFlag to inform the other
145 // threads that the socket is closed. 134 // threads that the socket is closed.
146 rwLock.AcquireWriterLock(-1); 135 m_shutdownFlag = true;
147 shutdownFlag = true; 136 m_udpSocket.Close();
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 } 137 }
167 } 138 }
168 139
169 /// <summary>
170 ///
171 /// </summary>
172 public bool IsRunning
173 {
174 get { return !shutdownFlag; }
175 }
176
177 private void AsyncBeginReceive() 140 private void AsyncBeginReceive()
178 { 141 {
179 // this method actually kicks off the async read on the socket. 142 // allocate a packet buffer
180 // we aquire a reader lock here to ensure that no other thread 143 //WrappedObject<UDPPacketBuffer> wrappedBuffer = Pool.CheckOut();
181 // is trying to set shutdownFlag and close the socket. 144 UDPPacketBuffer buf = new UDPPacketBuffer();
182 rwLock.AcquireReaderLock(-1);
183 145
184 if (!shutdownFlag) 146 if (!m_shutdownFlag)
185 { 147 {
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 148 try
194 { 149 {
195 // kick off an async read 150 // kick off an async read
196 udpSocket.BeginReceiveFrom( 151 m_udpSocket.BeginReceiveFrom(
197 //wrappedBuffer.Instance.Data, 152 //wrappedBuffer.Instance.Data,
198 buf.Data, 153 buf.Data,
199 0, 154 0,
200 UDPPacketBuffer.BUFFER_SIZE, 155 UDPPacketBuffer.BUFFER_SIZE,
201 SocketFlags.None, 156 SocketFlags.None,
202 //ref wrappedBuffer.Instance.RemoteEndPoint,
203 ref buf.RemoteEndPoint, 157 ref buf.RemoteEndPoint,
204 new AsyncCallback(AsyncEndReceive), 158 AsyncEndReceive,
205 //wrappedBuffer); 159 //wrappedBuffer);
206 buf); 160 buf);
207 } 161 }
208 catch (SocketException) 162 catch (SocketException e)
209 { 163 {
210 // something bad happened 164 if (e.SocketErrorCode == SocketError.ConnectionReset)
211 //Logger.Log( 165 {
212 // "A SocketException occurred in UDPServer.AsyncBeginReceive()", 166 m_log.Warn("[UDPBASE]: SIO_UDP_CONNRESET was ignored, attempting to salvage the UDP listener on port " + m_udpPort);
213 // Helpers.LogLevel.Error, se); 167 bool salvaged = false;
214 168 while (!salvaged)
215 // an error occurred, therefore the operation is void. Decrement the reference count. 169 {
216 Interlocked.Decrement(ref rwOperationCount); 170 try
171 {
172 m_udpSocket.BeginReceiveFrom(
173 //wrappedBuffer.Instance.Data,
174 buf.Data,
175 0,
176 UDPPacketBuffer.BUFFER_SIZE,
177 SocketFlags.None,
178 ref buf.RemoteEndPoint,
179 AsyncEndReceive,
180 //wrappedBuffer);
181 buf);
182 salvaged = true;
183 }
184 catch (SocketException) { }
185 catch (ObjectDisposedException) { return; }
186 }
187
188 m_log.Warn("[UDPBASE]: Salvaged the UDP listener on port " + m_udpPort);
189 }
217 } 190 }
191 catch (ObjectDisposedException) { }
218 } 192 }
219
220 // we're done with the socket for now, release the reader lock.
221 rwLock.ReleaseReaderLock();
222 } 193 }
223 194
224 private void AsyncEndReceive(IAsyncResult iar) 195 private void AsyncEndReceive(IAsyncResult iar)
225 { 196 {
226 // Asynchronous receive operations will complete here through the call 197 // Asynchronous receive operations will complete here through the call
227 // to AsyncBeginReceive 198 // to AsyncBeginReceive
228 199 if (!m_shutdownFlag)
229 // aquire a reader lock
230 rwLock.AcquireReaderLock(-1);
231
232 if (!shutdownFlag)
233 { 200 {
234 // get the buffer that was created in AsyncBeginReceive 201 // get the buffer that was created in AsyncBeginReceive
235 // this is the received data 202 // this is the received data
@@ -241,100 +208,56 @@ namespace OpenSim.Region.ClientStack.LindenUDP
241 { 208 {
242 // get the length of data actually read from the socket, store it with the 209 // get the length of data actually read from the socket, store it with the
243 // buffer 210 // buffer
244 buffer.DataLength = udpSocket.EndReceiveFrom(iar, ref buffer.RemoteEndPoint); 211 buffer.DataLength = m_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 212
252 // call the abstract method PacketReceived(), passing the buffer that 213 // call the abstract method PacketReceived(), passing the buffer that
253 // has just been filled from the socket read. 214 // has just been filled from the socket read.
254 PacketReceived(buffer); 215 PacketReceived(buffer);
255 } 216 }
256 catch (SocketException) 217 catch (SocketException) { }
257 { 218 catch (ObjectDisposedException) { }
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 219 finally
265 { 220 {
221 // wrappedBuffer.Dispose();
222
266 // start another receive - this keeps the server going! 223 // start another receive - this keeps the server going!
267 AsyncBeginReceive(); 224 AsyncBeginReceive();
268
269 //wrappedBuffer.Dispose();
270 } 225 }
271 } 226
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 } 227 }
279 } 228 }
280 229
281 public void AsyncBeginSend(UDPPacketBuffer buf) 230 public void AsyncBeginSend(UDPPacketBuffer buf)
282 { 231 {
283 rwLock.AcquireReaderLock(-1); 232 if (!m_shutdownFlag)
284
285 if (!shutdownFlag)
286 { 233 {
287 try 234 try
288 { 235 {
289 Interlocked.Increment(ref rwOperationCount); 236 m_udpSocket.BeginSendTo(
290 udpSocket.BeginSendTo(
291 buf.Data, 237 buf.Data,
292 0, 238 0,
293 buf.DataLength, 239 buf.DataLength,
294 SocketFlags.None, 240 SocketFlags.None,
295 buf.RemoteEndPoint, 241 buf.RemoteEndPoint,
296 new AsyncCallback(AsyncEndSend), 242 AsyncEndSend,
297 buf); 243 buf);
298 } 244 }
299 catch (SocketException) 245 catch (SocketException) { }
300 { 246 catch (ObjectDisposedException) { }
301 //Logger.Log(
302 // "A SocketException occurred in UDPServer.AsyncBeginSend()",
303 // Helpers.LogLevel.Error, se);
304 }
305 } 247 }
306
307 rwLock.ReleaseReaderLock();
308 } 248 }
309 249
310 private void AsyncEndSend(IAsyncResult iar) 250 void AsyncEndSend(IAsyncResult result)
311 { 251 {
312 rwLock.AcquireReaderLock(-1); 252 try
313
314 if (!shutdownFlag)
315 { 253 {
316 UDPPacketBuffer buffer = (UDPPacketBuffer)iar.AsyncState; 254 UDPPacketBuffer buf = (UDPPacketBuffer)result.AsyncState;
255 int bytesSent = m_udpSocket.EndSendTo(result);
317 256
318 try 257 PacketSent(buf, bytesSent);
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 } 258 }
335 259 catch (SocketException) { }
336 Interlocked.Decrement(ref rwOperationCount); 260 catch (ObjectDisposedException) { }
337 rwLock.ReleaseReaderLock();
338 } 261 }
339 } 262 }
340} 263}