diff options
Diffstat (limited to 'OpenSim/Region/ClientStack/Linden/UDP/OpenSimUDPBase.cs')
-rw-r--r-- | OpenSim/Region/ClientStack/Linden/UDP/OpenSimUDPBase.cs | 206 |
1 files changed, 185 insertions, 21 deletions
diff --git a/OpenSim/Region/ClientStack/Linden/UDP/OpenSimUDPBase.cs b/OpenSim/Region/ClientStack/Linden/UDP/OpenSimUDPBase.cs index f143c32..f62dc15 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/OpenSimUDPBase.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/OpenSimUDPBase.cs | |||
@@ -78,6 +78,92 @@ namespace OpenMetaverse | |||
78 | public bool IsRunningOutbound { get; private set; } | 78 | public bool IsRunningOutbound { get; private set; } |
79 | 79 | ||
80 | /// <summary> | 80 | /// <summary> |
81 | /// Number of UDP receives. | ||
82 | /// </summary> | ||
83 | public int UdpReceives { get; private set; } | ||
84 | |||
85 | /// <summary> | ||
86 | /// Number of UDP sends | ||
87 | /// </summary> | ||
88 | public int UdpSends { get; private set; } | ||
89 | |||
90 | /// <summary> | ||
91 | /// Number of receives over which to establish a receive time average. | ||
92 | /// </summary> | ||
93 | private readonly static int s_receiveTimeSamples = 500; | ||
94 | |||
95 | /// <summary> | ||
96 | /// Current number of samples taken to establish a receive time average. | ||
97 | /// </summary> | ||
98 | private int m_currentReceiveTimeSamples; | ||
99 | |||
100 | /// <summary> | ||
101 | /// Cumulative receive time for the sample so far. | ||
102 | /// </summary> | ||
103 | private int m_receiveTicksInCurrentSamplePeriod; | ||
104 | |||
105 | /// <summary> | ||
106 | /// The average time taken for each require receive in the last sample. | ||
107 | /// </summary> | ||
108 | public float AverageReceiveTicksForLastSamplePeriod { get; private set; } | ||
109 | |||
110 | #region PacketDropDebugging | ||
111 | /// <summary> | ||
112 | /// For debugging purposes only... random number generator for dropping | ||
113 | /// outbound packets. | ||
114 | /// </summary> | ||
115 | private Random m_dropRandomGenerator = new Random(); | ||
116 | |||
117 | /// <summary> | ||
118 | /// For debugging purposes only... parameters for a simplified | ||
119 | /// model of packet loss with bursts, overall drop rate should | ||
120 | /// be roughly 1 - m_dropLengthProbability / (m_dropProbabiliy + m_dropLengthProbability) | ||
121 | /// which is about 1% for parameters 0.0015 and 0.15 | ||
122 | /// </summary> | ||
123 | private double m_dropProbability = 0.0030; | ||
124 | private double m_dropLengthProbability = 0.15; | ||
125 | private bool m_dropState = false; | ||
126 | |||
127 | /// <summary> | ||
128 | /// For debugging purposes only... parameters to control the time | ||
129 | /// duration over which packet loss bursts can occur, if no packets | ||
130 | /// have been sent for m_dropResetTicks milliseconds, then reset the | ||
131 | /// state of the packet dropper to its default. | ||
132 | /// </summary> | ||
133 | private int m_dropLastTick = 0; | ||
134 | private int m_dropResetTicks = 500; | ||
135 | |||
136 | /// <summary> | ||
137 | /// Debugging code used to simulate dropped packets with bursts | ||
138 | /// </summary> | ||
139 | private bool DropOutgoingPacket() | ||
140 | { | ||
141 | double rnum = m_dropRandomGenerator.NextDouble(); | ||
142 | |||
143 | // if the connection has been idle for awhile (more than m_dropResetTicks) then | ||
144 | // reset the state to the default state, don't continue a burst | ||
145 | int curtick = Util.EnvironmentTickCount(); | ||
146 | if (Util.EnvironmentTickCountSubtract(curtick, m_dropLastTick) > m_dropResetTicks) | ||
147 | m_dropState = false; | ||
148 | |||
149 | m_dropLastTick = curtick; | ||
150 | |||
151 | // if we are dropping packets, then the probability of dropping | ||
152 | // this packet is the probability that we stay in the burst | ||
153 | if (m_dropState) | ||
154 | { | ||
155 | m_dropState = (rnum < (1.0 - m_dropLengthProbability)) ? true : false; | ||
156 | } | ||
157 | else | ||
158 | { | ||
159 | m_dropState = (rnum < m_dropProbability) ? true : false; | ||
160 | } | ||
161 | |||
162 | return m_dropState; | ||
163 | } | ||
164 | #endregion PacketDropDebugging | ||
165 | |||
166 | /// <summary> | ||
81 | /// Default constructor | 167 | /// Default constructor |
82 | /// </summary> | 168 | /// </summary> |
83 | /// <param name="bindAddress">Local IP address to bind the server to</param> | 169 | /// <param name="bindAddress">Local IP address to bind the server to</param> |
@@ -87,6 +173,10 @@ namespace OpenMetaverse | |||
87 | { | 173 | { |
88 | m_localBindAddress = bindAddress; | 174 | m_localBindAddress = bindAddress; |
89 | m_udpPort = port; | 175 | m_udpPort = port; |
176 | |||
177 | // for debugging purposes only, initializes the random number generator | ||
178 | // used for simulating packet loss | ||
179 | // m_dropRandomGenerator = new Random(); | ||
90 | } | 180 | } |
91 | 181 | ||
92 | /// <summary> | 182 | /// <summary> |
@@ -105,12 +195,14 @@ namespace OpenMetaverse | |||
105 | /// manner (not throwing an exception when the remote side resets the | 195 | /// manner (not throwing an exception when the remote side resets the |
106 | /// connection). This call is ignored on Mono where the flag is not | 196 | /// connection). This call is ignored on Mono where the flag is not |
107 | /// necessary</remarks> | 197 | /// necessary</remarks> |
108 | public void StartInbound(int recvBufferSize, bool asyncPacketHandling) | 198 | public virtual void StartInbound(int recvBufferSize, bool asyncPacketHandling) |
109 | { | 199 | { |
110 | m_asyncPacketHandling = asyncPacketHandling; | 200 | m_asyncPacketHandling = asyncPacketHandling; |
111 | 201 | ||
112 | if (!IsRunningInbound) | 202 | if (!IsRunningInbound) |
113 | { | 203 | { |
204 | m_log.DebugFormat("[UDPBASE]: Starting inbound UDP loop"); | ||
205 | |||
114 | const int SIO_UDP_CONNRESET = -1744830452; | 206 | const int SIO_UDP_CONNRESET = -1744830452; |
115 | 207 | ||
116 | IPEndPoint ipep = new IPEndPoint(m_localBindAddress, m_udpPort); | 208 | IPEndPoint ipep = new IPEndPoint(m_localBindAddress, m_udpPort); |
@@ -126,6 +218,17 @@ namespace OpenMetaverse | |||
126 | 218 | ||
127 | try | 219 | try |
128 | { | 220 | { |
221 | if (m_udpSocket.Ttl < 128) | ||
222 | { | ||
223 | m_udpSocket.Ttl = 128; | ||
224 | } | ||
225 | } | ||
226 | catch (SocketException) | ||
227 | { | ||
228 | m_log.Debug("[UDPBASE]: Failed to increase default TTL"); | ||
229 | } | ||
230 | try | ||
231 | { | ||
129 | // This udp socket flag is not supported under mono, | 232 | // This udp socket flag is not supported under mono, |
130 | // so we'll catch the exception and continue | 233 | // so we'll catch the exception and continue |
131 | m_udpSocket.IOControl(SIO_UDP_CONNRESET, new byte[] { 0 }, null); | 234 | m_udpSocket.IOControl(SIO_UDP_CONNRESET, new byte[] { 0 }, null); |
@@ -136,6 +239,12 @@ namespace OpenMetaverse | |||
136 | m_log.Debug("[UDPBASE]: SIO_UDP_CONNRESET flag not supported on this platform, ignoring"); | 239 | m_log.Debug("[UDPBASE]: SIO_UDP_CONNRESET flag not supported on this platform, ignoring"); |
137 | } | 240 | } |
138 | 241 | ||
242 | // On at least Mono 3.2.8, multiple UDP sockets can bind to the same port by default. At the moment | ||
243 | // we never want two regions to listen on the same port as they cannot demultiplex each other's messages, | ||
244 | // leading to a confusing bug. | ||
245 | // By default, Windows does not allow two sockets to bind to the same port. | ||
246 | m_udpSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, false); | ||
247 | |||
139 | if (recvBufferSize != 0) | 248 | if (recvBufferSize != 0) |
140 | m_udpSocket.ReceiveBufferSize = recvBufferSize; | 249 | m_udpSocket.ReceiveBufferSize = recvBufferSize; |
141 | 250 | ||
@@ -153,30 +262,32 @@ namespace OpenMetaverse | |||
153 | /// <summary> | 262 | /// <summary> |
154 | /// Start outbound UDP packet handling. | 263 | /// Start outbound UDP packet handling. |
155 | /// </summary> | 264 | /// </summary> |
156 | public void StartOutbound() | 265 | public virtual void StartOutbound() |
157 | { | 266 | { |
267 | m_log.DebugFormat("[UDPBASE]: Starting outbound UDP loop"); | ||
268 | |||
158 | IsRunningOutbound = true; | 269 | IsRunningOutbound = true; |
159 | } | 270 | } |
160 | 271 | ||
161 | public void StopInbound() | 272 | public virtual void StopInbound() |
162 | { | 273 | { |
163 | if (IsRunningInbound) | 274 | if (IsRunningInbound) |
164 | { | 275 | { |
165 | // wait indefinitely for a writer lock. Once this is called, the .NET runtime | 276 | m_log.DebugFormat("[UDPBASE]: Stopping inbound UDP loop"); |
166 | // will deny any more reader locks, in effect blocking all other send/receive | 277 | |
167 | // threads. Once we have the lock, we set IsRunningInbound = false to inform the other | ||
168 | // threads that the socket is closed. | ||
169 | IsRunningInbound = false; | 278 | IsRunningInbound = false; |
170 | m_udpSocket.Close(); | 279 | m_udpSocket.Close(); |
171 | } | 280 | } |
172 | } | 281 | } |
173 | 282 | ||
174 | public void StopOutbound() | 283 | public virtual void StopOutbound() |
175 | { | 284 | { |
285 | m_log.DebugFormat("[UDPBASE]: Stopping outbound UDP loop"); | ||
286 | |||
176 | IsRunningOutbound = false; | 287 | IsRunningOutbound = false; |
177 | } | 288 | } |
178 | 289 | ||
179 | protected virtual bool EnablePools() | 290 | public virtual bool EnablePools() |
180 | { | 291 | { |
181 | if (!UsePools) | 292 | if (!UsePools) |
182 | { | 293 | { |
@@ -190,7 +301,7 @@ namespace OpenMetaverse | |||
190 | return false; | 301 | return false; |
191 | } | 302 | } |
192 | 303 | ||
193 | protected virtual bool DisablePools() | 304 | public virtual bool DisablePools() |
194 | { | 305 | { |
195 | if (UsePools) | 306 | if (UsePools) |
196 | { | 307 | { |
@@ -261,7 +372,16 @@ namespace OpenMetaverse | |||
261 | m_log.Warn("[UDPBASE]: Salvaged the UDP listener on port " + m_udpPort); | 372 | m_log.Warn("[UDPBASE]: Salvaged the UDP listener on port " + m_udpPort); |
262 | } | 373 | } |
263 | } | 374 | } |
264 | catch (ObjectDisposedException) { } | 375 | catch (ObjectDisposedException e) |
376 | { | ||
377 | m_log.Error( | ||
378 | string.Format("[UDPBASE]: Error processing UDP begin receive {0}. Exception ", UdpReceives), e); | ||
379 | } | ||
380 | catch (Exception e) | ||
381 | { | ||
382 | m_log.Error( | ||
383 | string.Format("[UDPBASE]: Error processing UDP begin receive {0}. Exception ", UdpReceives), e); | ||
384 | } | ||
265 | } | 385 | } |
266 | } | 386 | } |
267 | 387 | ||
@@ -271,17 +391,21 @@ namespace OpenMetaverse | |||
271 | // to AsyncBeginReceive | 391 | // to AsyncBeginReceive |
272 | if (IsRunningInbound) | 392 | if (IsRunningInbound) |
273 | { | 393 | { |
394 | UdpReceives++; | ||
395 | |||
274 | // Asynchronous mode will start another receive before the | 396 | // Asynchronous mode will start another receive before the |
275 | // callback for this packet is even fired. Very parallel :-) | 397 | // callback for this packet is even fired. Very parallel :-) |
276 | if (m_asyncPacketHandling) | 398 | if (m_asyncPacketHandling) |
277 | AsyncBeginReceive(); | 399 | AsyncBeginReceive(); |
278 | 400 | ||
279 | // get the buffer that was created in AsyncBeginReceive | ||
280 | // this is the received data | ||
281 | UDPPacketBuffer buffer = (UDPPacketBuffer)iar.AsyncState; | ||
282 | |||
283 | try | 401 | try |
284 | { | 402 | { |
403 | // get the buffer that was created in AsyncBeginReceive | ||
404 | // this is the received data | ||
405 | UDPPacketBuffer buffer = (UDPPacketBuffer)iar.AsyncState; | ||
406 | |||
407 | int startTick = Util.EnvironmentTickCount(); | ||
408 | |||
285 | // get the length of data actually read from the socket, store it with the | 409 | // get the length of data actually read from the socket, store it with the |
286 | // buffer | 410 | // buffer |
287 | buffer.DataLength = m_udpSocket.EndReceiveFrom(iar, ref buffer.RemoteEndPoint); | 411 | buffer.DataLength = m_udpSocket.EndReceiveFrom(iar, ref buffer.RemoteEndPoint); |
@@ -289,9 +413,42 @@ namespace OpenMetaverse | |||
289 | // call the abstract method PacketReceived(), passing the buffer that | 413 | // call the abstract method PacketReceived(), passing the buffer that |
290 | // has just been filled from the socket read. | 414 | // has just been filled from the socket read. |
291 | PacketReceived(buffer); | 415 | PacketReceived(buffer); |
416 | |||
417 | // If more than one thread can be calling AsyncEndReceive() at once (e.g. if m_asyncPacketHandler) | ||
418 | // then a particular stat may be inaccurate due to a race condition. We won't worry about this | ||
419 | // since this should be rare and won't cause a runtime problem. | ||
420 | if (m_currentReceiveTimeSamples >= s_receiveTimeSamples) | ||
421 | { | ||
422 | AverageReceiveTicksForLastSamplePeriod | ||
423 | = (float)m_receiveTicksInCurrentSamplePeriod / s_receiveTimeSamples; | ||
424 | |||
425 | m_receiveTicksInCurrentSamplePeriod = 0; | ||
426 | m_currentReceiveTimeSamples = 0; | ||
427 | } | ||
428 | else | ||
429 | { | ||
430 | m_receiveTicksInCurrentSamplePeriod += Util.EnvironmentTickCountSubtract(startTick); | ||
431 | m_currentReceiveTimeSamples++; | ||
432 | } | ||
433 | } | ||
434 | catch (SocketException se) | ||
435 | { | ||
436 | m_log.Error( | ||
437 | string.Format( | ||
438 | "[UDPBASE]: Error processing UDP end receive {0}, socket error code {1}. Exception ", | ||
439 | UdpReceives, se.ErrorCode), | ||
440 | se); | ||
441 | } | ||
442 | catch (ObjectDisposedException e) | ||
443 | { | ||
444 | m_log.Error( | ||
445 | string.Format("[UDPBASE]: Error processing UDP end receive {0}. Exception ", UdpReceives), e); | ||
446 | } | ||
447 | catch (Exception e) | ||
448 | { | ||
449 | m_log.Error( | ||
450 | string.Format("[UDPBASE]: Error processing UDP end receive {0}. Exception ", UdpReceives), e); | ||
292 | } | 451 | } |
293 | catch (SocketException) { } | ||
294 | catch (ObjectDisposedException) { } | ||
295 | finally | 452 | finally |
296 | { | 453 | { |
297 | // if (UsePools) | 454 | // if (UsePools) |
@@ -302,14 +459,19 @@ namespace OpenMetaverse | |||
302 | if (!m_asyncPacketHandling) | 459 | if (!m_asyncPacketHandling) |
303 | AsyncBeginReceive(); | 460 | AsyncBeginReceive(); |
304 | } | 461 | } |
305 | |||
306 | } | 462 | } |
307 | } | 463 | } |
308 | 464 | ||
309 | public void AsyncBeginSend(UDPPacketBuffer buf) | 465 | public void AsyncBeginSend(UDPPacketBuffer buf) |
310 | { | 466 | { |
311 | if (IsRunningOutbound) | 467 | // if (IsRunningOutbound) |
312 | { | 468 | // { |
469 | |||
470 | // This is strictly for debugging purposes to simulate dropped | ||
471 | // packets when testing throttles & retransmission code | ||
472 | // if (DropOutgoingPacket()) | ||
473 | // return; | ||
474 | |||
313 | try | 475 | try |
314 | { | 476 | { |
315 | m_udpSocket.BeginSendTo( | 477 | m_udpSocket.BeginSendTo( |
@@ -323,7 +485,7 @@ namespace OpenMetaverse | |||
323 | } | 485 | } |
324 | catch (SocketException) { } | 486 | catch (SocketException) { } |
325 | catch (ObjectDisposedException) { } | 487 | catch (ObjectDisposedException) { } |
326 | } | 488 | // } |
327 | } | 489 | } |
328 | 490 | ||
329 | void AsyncEndSend(IAsyncResult result) | 491 | void AsyncEndSend(IAsyncResult result) |
@@ -332,6 +494,8 @@ namespace OpenMetaverse | |||
332 | { | 494 | { |
333 | // UDPPacketBuffer buf = (UDPPacketBuffer)result.AsyncState; | 495 | // UDPPacketBuffer buf = (UDPPacketBuffer)result.AsyncState; |
334 | m_udpSocket.EndSendTo(result); | 496 | m_udpSocket.EndSendTo(result); |
497 | |||
498 | UdpSends++; | ||
335 | } | 499 | } |
336 | catch (SocketException) { } | 500 | catch (SocketException) { } |
337 | catch (ObjectDisposedException) { } | 501 | catch (ObjectDisposedException) { } |