corrade-vassal – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 vero 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  
33 namespace OpenMetaverse
34 {
35 /// <summary>
36 ///
37 /// </summary>
38 public abstract class UDPBase
39 {
40 // these abstract methods must be implemented in a derived class to actually do
41 // something with the packets that are sent and received.
42 protected abstract void PacketReceived(UDPPacketBuffer buffer);
43 protected abstract void PacketSent(UDPPacketBuffer buffer, int bytesSent);
44  
45 // the port to listen on
46 protected int udpPort;
47  
48 // the remote endpoint to communicate with
49 protected IPEndPoint remoteEndPoint = null;
50  
51 // the UDP socket
52 private Socket udpSocket;
53  
54 // the all important shutdownFlag.
55 private volatile bool shutdownFlag = true;
56  
57 /// <summary>
58 /// Initialize the UDP packet handler in server mode
59 /// </summary>
60 /// <param name="port">Port to listening for incoming UDP packets on</param>
61 public UDPBase(int port)
62 {
63 udpPort = port;
64 }
65  
66 /// <summary>
67 /// Initialize the UDP packet handler in client mode
68 /// </summary>
69 /// <param name="endPoint">Remote UDP server to connect to</param>
70 public UDPBase(IPEndPoint endPoint)
71 {
72 remoteEndPoint = endPoint;
73 udpPort = 0;
74 }
75  
76 /// <summary>
77 ///
78 /// </summary>
79 public void Start()
80 {
81 if (shutdownFlag)
82 {
83 const int SIO_UDP_CONNRESET = -1744830452;
84  
85 IPEndPoint ipep = new IPEndPoint(Settings.BIND_ADDR, udpPort);
86 udpSocket = new Socket(
87 AddressFamily.InterNetwork,
88 SocketType.Dgram,
89 ProtocolType.Udp);
90 try
91 {
92 // this udp socket flag is not supported under mono,
93 // so we'll catch the exception and continue
94 udpSocket.IOControl(SIO_UDP_CONNRESET, new byte[] { 0 }, null);
95 }
96 catch (SocketException)
97 {
98 Logger.DebugLog("UDP SIO_UDP_CONNRESET flag not supported on this platform");
99 }
100  
101 // On at least Mono 3.2.8, multiple UDP sockets can bind to the same port by default. This means that
102 // when running multiple connections, two can occasionally bind to the same port, leading to unexpected
103 // errors as they intercept each others messages. We need to prevent this. This is not allowed by
104 // default on Windows.
105 udpSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, false);
106  
107 udpSocket.Bind(ipep);
108  
109 // we're not shutting down, we're starting up
110 shutdownFlag = false;
111  
112 // kick off an async receive. The Start() method will return, the
113 // actual receives will occur asynchronously and will be caught in
114 // AsyncEndRecieve().
115 AsyncBeginReceive();
116 }
117 }
118  
119 /// <summary>
120 ///
121 /// </summary>
122 public void Stop()
123 {
124 if (!shutdownFlag)
125 {
126 // wait indefinitely for a writer lock. Once this is called, the .NET runtime
127 // will deny any more reader locks, in effect blocking all other send/receive
128 // threads. Once we have the lock, we set shutdownFlag to inform the other
129 // threads that the socket is closed.
130 shutdownFlag = true;
131 udpSocket.Close();
132 }
133 }
134  
135 /// <summary>
136 ///
137 /// </summary>
138 public bool IsRunning
139 {
140 get { return !shutdownFlag; }
141 }
142  
143 private void AsyncBeginReceive()
144 {
145 // allocate a packet buffer
146 //WrappedObject<UDPPacketBuffer> wrappedBuffer = Pool.CheckOut();
147 UDPPacketBuffer buf = new UDPPacketBuffer();
148  
149 if (!shutdownFlag)
150 {
151 try
152 {
153 // kick off an async read
154 udpSocket.BeginReceiveFrom(
155 //wrappedBuffer.Instance.Data,
156 buf.Data,
157 0,
158 UDPPacketBuffer.BUFFER_SIZE,
159 SocketFlags.None,
160 ref buf.RemoteEndPoint,
161 AsyncEndReceive,
162 //wrappedBuffer);
163 buf);
164 }
165 catch (SocketException e)
166 {
167 if (e.SocketErrorCode == SocketError.ConnectionReset)
168 {
169 Logger.Log("SIO_UDP_CONNRESET was ignored, attempting to salvage the UDP listener on port " + udpPort, Helpers.LogLevel.Error);
170 bool salvaged = false;
171 while (!salvaged)
172 {
173 try
174 {
175 udpSocket.BeginReceiveFrom(
176 //wrappedBuffer.Instance.Data,
177 buf.Data,
178 0,
179 UDPPacketBuffer.BUFFER_SIZE,
180 SocketFlags.None,
181 ref buf.RemoteEndPoint,
182 AsyncEndReceive,
183 //wrappedBuffer);
184 buf);
185 salvaged = true;
186 }
187 catch (SocketException) { }
188 catch (ObjectDisposedException) { return; }
189 }
190  
191 Logger.Log("Salvaged the UDP listener on port " + udpPort, Helpers.LogLevel.Info);
192 }
193 }
194 catch (ObjectDisposedException) { }
195 }
196 }
197  
198 private void AsyncEndReceive(IAsyncResult iar)
199 {
200 // Asynchronous receive operations will complete here through the call
201 // to AsyncBeginReceive
202 if (!shutdownFlag)
203 {
204 // start another receive - this keeps the server going!
205 AsyncBeginReceive();
206  
207 // get the buffer that was created in AsyncBeginReceive
208 // this is the received data
209 //WrappedObject<UDPPacketBuffer> wrappedBuffer = (WrappedObject<UDPPacketBuffer>)iar.AsyncState;
210 //UDPPacketBuffer buffer = wrappedBuffer.Instance;
211 UDPPacketBuffer buffer = (UDPPacketBuffer)iar.AsyncState;
212  
213 try
214 {
215 // get the length of data actually read from the socket, store it with the
216 // buffer
217 buffer.DataLength = udpSocket.EndReceiveFrom(iar, ref buffer.RemoteEndPoint);
218  
219 // call the abstract method PacketReceived(), passing the buffer that
220 // has just been filled from the socket read.
221 PacketReceived(buffer);
222 }
223 catch (SocketException) { }
224 catch (ObjectDisposedException) { }
225 //finally { wrappedBuffer.Dispose(); }
226 }
227 }
228  
229 public void AsyncBeginSend(UDPPacketBuffer buf)
230 {
231 if (!shutdownFlag)
232 {
233 try
234 {
235 // Profiling heavily loaded clients was showing better performance with
236 // synchronous UDP packet sending
237 udpSocket.SendTo(
238 buf.Data,
239 0,
240 buf.DataLength,
241 SocketFlags.None,
242 buf.RemoteEndPoint);
243  
244 //udpSocket.BeginSendTo(
245 // buf.Data,
246 // 0,
247 // buf.DataLength,
248 // SocketFlags.None,
249 // buf.RemoteEndPoint,
250 // AsyncEndSend,
251 // buf);
252 }
253 catch (SocketException) { }
254 catch (ObjectDisposedException) { }
255 }
256 }
257  
258 //void AsyncEndSend(IAsyncResult result)
259 //{
260 // try
261 // {
262 // UDPPacketBuffer buf = (UDPPacketBuffer)result.AsyncState;
263 // if (!udpSocket.Connected) return;
264 // int bytesSent = udpSocket.EndSendTo(result);
265  
266 // PacketSent(buf, bytesSent);
267 // }
268 // catch (SocketException) { }
269 // catch (ObjectDisposedException) { }
270 //}
271 }
272 }