From 134f86e8d5c414409631b25b8c6f0ee45fbd8631 Mon Sep 17 00:00:00 2001 From: David Walter Seikel Date: Thu, 3 Nov 2016 21:44:39 +1000 Subject: Initial update to OpenSim 0.8.2.1 source code. --- .../ClientStack/Linden/UDP/OpenSimUDPBase.cs | 206 ++++++++++++++++++--- 1 file changed, 185 insertions(+), 21 deletions(-) (limited to 'OpenSim/Region/ClientStack/Linden/UDP/OpenSimUDPBase.cs') 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 public bool IsRunningOutbound { get; private set; } /// + /// Number of UDP receives. + /// + public int UdpReceives { get; private set; } + + /// + /// Number of UDP sends + /// + public int UdpSends { get; private set; } + + /// + /// Number of receives over which to establish a receive time average. + /// + private readonly static int s_receiveTimeSamples = 500; + + /// + /// Current number of samples taken to establish a receive time average. + /// + private int m_currentReceiveTimeSamples; + + /// + /// Cumulative receive time for the sample so far. + /// + private int m_receiveTicksInCurrentSamplePeriod; + + /// + /// The average time taken for each require receive in the last sample. + /// + public float AverageReceiveTicksForLastSamplePeriod { get; private set; } + + #region PacketDropDebugging + /// + /// For debugging purposes only... random number generator for dropping + /// outbound packets. + /// + private Random m_dropRandomGenerator = new Random(); + + /// + /// For debugging purposes only... parameters for a simplified + /// model of packet loss with bursts, overall drop rate should + /// be roughly 1 - m_dropLengthProbability / (m_dropProbabiliy + m_dropLengthProbability) + /// which is about 1% for parameters 0.0015 and 0.15 + /// + private double m_dropProbability = 0.0030; + private double m_dropLengthProbability = 0.15; + private bool m_dropState = false; + + /// + /// For debugging purposes only... parameters to control the time + /// duration over which packet loss bursts can occur, if no packets + /// have been sent for m_dropResetTicks milliseconds, then reset the + /// state of the packet dropper to its default. + /// + private int m_dropLastTick = 0; + private int m_dropResetTicks = 500; + + /// + /// Debugging code used to simulate dropped packets with bursts + /// + private bool DropOutgoingPacket() + { + double rnum = m_dropRandomGenerator.NextDouble(); + + // if the connection has been idle for awhile (more than m_dropResetTicks) then + // reset the state to the default state, don't continue a burst + int curtick = Util.EnvironmentTickCount(); + if (Util.EnvironmentTickCountSubtract(curtick, m_dropLastTick) > m_dropResetTicks) + m_dropState = false; + + m_dropLastTick = curtick; + + // if we are dropping packets, then the probability of dropping + // this packet is the probability that we stay in the burst + if (m_dropState) + { + m_dropState = (rnum < (1.0 - m_dropLengthProbability)) ? true : false; + } + else + { + m_dropState = (rnum < m_dropProbability) ? true : false; + } + + return m_dropState; + } + #endregion PacketDropDebugging + + /// /// Default constructor /// /// Local IP address to bind the server to @@ -87,6 +173,10 @@ namespace OpenMetaverse { m_localBindAddress = bindAddress; m_udpPort = port; + + // for debugging purposes only, initializes the random number generator + // used for simulating packet loss + // m_dropRandomGenerator = new Random(); } /// @@ -105,12 +195,14 @@ namespace OpenMetaverse /// manner (not throwing an exception when the remote side resets the /// connection). This call is ignored on Mono where the flag is not /// necessary - public void StartInbound(int recvBufferSize, bool asyncPacketHandling) + public virtual void StartInbound(int recvBufferSize, bool asyncPacketHandling) { m_asyncPacketHandling = asyncPacketHandling; if (!IsRunningInbound) { + m_log.DebugFormat("[UDPBASE]: Starting inbound UDP loop"); + const int SIO_UDP_CONNRESET = -1744830452; IPEndPoint ipep = new IPEndPoint(m_localBindAddress, m_udpPort); @@ -126,6 +218,17 @@ namespace OpenMetaverse try { + if (m_udpSocket.Ttl < 128) + { + m_udpSocket.Ttl = 128; + } + } + catch (SocketException) + { + m_log.Debug("[UDPBASE]: Failed to increase default TTL"); + } + try + { // This udp socket flag is not supported under mono, // so we'll catch the exception and continue m_udpSocket.IOControl(SIO_UDP_CONNRESET, new byte[] { 0 }, null); @@ -136,6 +239,12 @@ namespace OpenMetaverse m_log.Debug("[UDPBASE]: SIO_UDP_CONNRESET flag not supported on this platform, ignoring"); } + // On at least Mono 3.2.8, multiple UDP sockets can bind to the same port by default. At the moment + // we never want two regions to listen on the same port as they cannot demultiplex each other's messages, + // leading to a confusing bug. + // By default, Windows does not allow two sockets to bind to the same port. + m_udpSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, false); + if (recvBufferSize != 0) m_udpSocket.ReceiveBufferSize = recvBufferSize; @@ -153,30 +262,32 @@ namespace OpenMetaverse /// /// Start outbound UDP packet handling. /// - public void StartOutbound() + public virtual void StartOutbound() { + m_log.DebugFormat("[UDPBASE]: Starting outbound UDP loop"); + IsRunningOutbound = true; } - public void StopInbound() + public virtual void StopInbound() { if (IsRunningInbound) { - // wait indefinitely for a writer lock. Once this is called, the .NET runtime - // will deny any more reader locks, in effect blocking all other send/receive - // threads. Once we have the lock, we set IsRunningInbound = false to inform the other - // threads that the socket is closed. + m_log.DebugFormat("[UDPBASE]: Stopping inbound UDP loop"); + IsRunningInbound = false; m_udpSocket.Close(); } } - public void StopOutbound() + public virtual void StopOutbound() { + m_log.DebugFormat("[UDPBASE]: Stopping outbound UDP loop"); + IsRunningOutbound = false; } - protected virtual bool EnablePools() + public virtual bool EnablePools() { if (!UsePools) { @@ -190,7 +301,7 @@ namespace OpenMetaverse return false; } - protected virtual bool DisablePools() + public virtual bool DisablePools() { if (UsePools) { @@ -261,7 +372,16 @@ namespace OpenMetaverse m_log.Warn("[UDPBASE]: Salvaged the UDP listener on port " + m_udpPort); } } - catch (ObjectDisposedException) { } + catch (ObjectDisposedException e) + { + m_log.Error( + string.Format("[UDPBASE]: Error processing UDP begin receive {0}. Exception ", UdpReceives), e); + } + catch (Exception e) + { + m_log.Error( + string.Format("[UDPBASE]: Error processing UDP begin receive {0}. Exception ", UdpReceives), e); + } } } @@ -271,17 +391,21 @@ namespace OpenMetaverse // to AsyncBeginReceive if (IsRunningInbound) { + UdpReceives++; + // Asynchronous mode will start another receive before the // callback for this packet is even fired. Very parallel :-) if (m_asyncPacketHandling) AsyncBeginReceive(); - // get the buffer that was created in AsyncBeginReceive - // this is the received data - UDPPacketBuffer buffer = (UDPPacketBuffer)iar.AsyncState; - try { + // get the buffer that was created in AsyncBeginReceive + // this is the received data + UDPPacketBuffer buffer = (UDPPacketBuffer)iar.AsyncState; + + int startTick = Util.EnvironmentTickCount(); + // get the length of data actually read from the socket, store it with the // buffer buffer.DataLength = m_udpSocket.EndReceiveFrom(iar, ref buffer.RemoteEndPoint); @@ -289,9 +413,42 @@ namespace OpenMetaverse // call the abstract method PacketReceived(), passing the buffer that // has just been filled from the socket read. PacketReceived(buffer); + + // If more than one thread can be calling AsyncEndReceive() at once (e.g. if m_asyncPacketHandler) + // then a particular stat may be inaccurate due to a race condition. We won't worry about this + // since this should be rare and won't cause a runtime problem. + if (m_currentReceiveTimeSamples >= s_receiveTimeSamples) + { + AverageReceiveTicksForLastSamplePeriod + = (float)m_receiveTicksInCurrentSamplePeriod / s_receiveTimeSamples; + + m_receiveTicksInCurrentSamplePeriod = 0; + m_currentReceiveTimeSamples = 0; + } + else + { + m_receiveTicksInCurrentSamplePeriod += Util.EnvironmentTickCountSubtract(startTick); + m_currentReceiveTimeSamples++; + } + } + catch (SocketException se) + { + m_log.Error( + string.Format( + "[UDPBASE]: Error processing UDP end receive {0}, socket error code {1}. Exception ", + UdpReceives, se.ErrorCode), + se); + } + catch (ObjectDisposedException e) + { + m_log.Error( + string.Format("[UDPBASE]: Error processing UDP end receive {0}. Exception ", UdpReceives), e); + } + catch (Exception e) + { + m_log.Error( + string.Format("[UDPBASE]: Error processing UDP end receive {0}. Exception ", UdpReceives), e); } - catch (SocketException) { } - catch (ObjectDisposedException) { } finally { // if (UsePools) @@ -302,14 +459,19 @@ namespace OpenMetaverse if (!m_asyncPacketHandling) AsyncBeginReceive(); } - } } public void AsyncBeginSend(UDPPacketBuffer buf) { - if (IsRunningOutbound) - { +// if (IsRunningOutbound) +// { + + // This is strictly for debugging purposes to simulate dropped + // packets when testing throttles & retransmission code + // if (DropOutgoingPacket()) + // return; + try { m_udpSocket.BeginSendTo( @@ -323,7 +485,7 @@ namespace OpenMetaverse } catch (SocketException) { } catch (ObjectDisposedException) { } - } +// } } void AsyncEndSend(IAsyncResult result) @@ -332,6 +494,8 @@ namespace OpenMetaverse { // UDPPacketBuffer buf = (UDPPacketBuffer)result.AsyncState; m_udpSocket.EndSendTo(result); + + UdpSends++; } catch (SocketException) { } catch (ObjectDisposedException) { } -- cgit v1.1