]> git.deb.at Git - rhonda/impostor.hazel.git/commitdiff
Have I been making mistakes this whole time?
authorForest <chocozilla@gmail.com>
Tue, 25 Dec 2018 07:46:06 +0000 (23:46 -0800)
committerForest <chocozilla@gmail.com>
Tue, 25 Dec 2018 07:46:06 +0000 (23:46 -0800)
Hazel/Connection.cs
Hazel/Udp/UdpClientConnection.cs
Hazel/Udp/UdpConnection.KeepAlive.cs
Hazel/Udp/UdpConnection.Reliable.cs
Hazel/Udp/UdpConnection.cs
Hazel/Udp/UdpConnectionListener.cs
Hazel/Udp/UdpServerConnection.cs

index 50014bafea5ee6bb75b28eb48567a89c32ad98eb..d300db129d4e0c988fd1efed06f5e07f702c79e4 100644 (file)
@@ -51,7 +51,7 @@ namespace Hazel
         public Action<DataReceivedEventArgs> DataReceived;
 
         public int TestLagMs = -1;
-
+        
         public event Action<byte[], int> DataSentRaw;
         protected void InvokeDataSentRaw(byte[] data, int length)
         {
@@ -106,7 +106,7 @@ namespace Hazel
         ///         Connections go round 4 states in their lifetime: they start as <see cref="ConnectionState.NotConnected"/> to 
         ///         indicate they have no endpoint, calling <see cref="Connect"/> takes them into 
         ///         <see cref="ConnectionState.Connecting"/>, once they have received confirmation they are connected they enter
-        ///         <see cref="ConnectionState.Connected"/> and finally calling <see cref="Close"/> sets them to 
+        ///         <see cref="ConnectionState.Connected"/> and finally calling <see cref="Dispose"/> sets them to 
         ///         <see cref="ConnectionState.Disconnecting"/> and then the sequence repeats back to
         ///         <see cref="ConnectionState.NotConnected"/> once disconnection is complete.
         ///     </para>
@@ -128,8 +128,8 @@ namespace Hazel
             
             protected set
             {
-                state = value;
-                if (state == ConnectionState.Connected)
+                this.state = value;
+                if (this.state == ConnectionState.Connected)
                     connectWaitLock.Set();
                 else
                     connectWaitLock.Reset();
@@ -152,9 +152,8 @@ namespace Hazel
         /// </remarks>
         protected Connection()
         {
-            Statistics = new ConnectionStatistics();
-
-            State = ConnectionState.NotConnected;
+            this.Statistics = new ConnectionStatistics();
+            this.State = ConnectionState.NotConnected;
         }
 
         /// <summary>
@@ -231,7 +230,7 @@ namespace Hazel
         /// <summary>
         ///     Sends a disconnect message to the end point.
         /// </summary>
-        public abstract void SendDisconnect();
+        protected abstract void SendDisconnect();
 
         /// <summary>
         ///     Invokes the DataReceived event.
@@ -295,24 +294,11 @@ namespace Hazel
         }
 
         /// <summary>
-        ///     Closes this connection safely.
+        /// For times when you want to force the disconnect handler to fire as well as close it.
+        /// If you only want to close it, just use Dispose.
         /// </summary>
-        /// <remarks>
-        ///     <para>
-        ///         Informs the end point of the connection that we are disconnecting from them and disposes of this 
-        ///         connection.
-        ///     </para>
-        ///     <para>
-        ///         This calls <see cref="Dispose()"/> and therefore sets <see cref="State"/> straight to 
-        ///         <see cref="ConnectionState.NotConnected"/>. Once you call Close you will not be able to send any more
-        ///         data using this connection and no more data will be received.
-        ///     </para> 
-        /// </remarks>
-        public virtual void Close()
-        {
-            Dispose();
-        }
-
+        public abstract void Disconnect(string reason);
+        
         /// <summary>
         ///     Disposes of this NetworkConnection.
         /// </summary>
index 0752d2c7b7b209014404d1a0136aedd38382c888..fb3a86f2164586174dcad97fdcbe7005cd4585e1 100644 (file)
@@ -93,11 +93,11 @@ namespace Hazel.Udp
                         }
                         catch (ObjectDisposedException)
                         {
-                            HandleDisconnect("Could not send as the socket was disposed of.");
+                            Disconnect("Could not send as the socket was disposed of.");
                         }
                         catch (SocketException)
                         {
-                            HandleDisconnect("Could not send data as a SocketException occured.");
+                            Disconnect("Could not send data as a SocketException occured.");
                         }
                     },
                     null
@@ -110,7 +110,35 @@ namespace Hazel.Udp
             }
             catch (SocketException)
             {
-                HandleDisconnect("Could not send data as a SocketException occured.");
+                Disconnect("Could not send data as a SocketException occured.");
+                throw;
+            }
+        }
+
+        protected override void WriteBytesToConnectionSync(byte[] bytes, int length)
+        {
+            InvokeDataSentRaw(bytes, length);
+
+            if (State != ConnectionState.Connected && State != ConnectionState.Connecting)
+                throw new InvalidOperationException("Could not send data as this Connection is not connected and is not connecting. Did you disconnect?");
+
+            try
+            {
+                socket.SendTo(
+                    bytes,
+                    0,
+                    length,
+                    SocketFlags.None,
+                    RemoteEndPoint);
+            }
+            catch (ObjectDisposedException)
+            {
+                //User probably called Disconnect in between this method starting and here so report the issue
+                throw new InvalidOperationException("Could not send data as this Connection is not connected. Did you disconnect?");
+            }
+            catch (SocketException)
+            {
+                Disconnect("Could not send data as a SocketException occured.");
                 throw;
             }
         }
@@ -203,14 +231,14 @@ namespace Hazel.Udp
             }
             catch (SocketException e)
             {
-                HandleDisconnect("A socket exception occured while reading data.");
+                Disconnect("Socket exception while reading data: " + e.Message);
                 return;
             }
 
             //Exit if no bytes read, we've failed.
             if (bytesReceived == 0)
             {
-                HandleDisconnect("Recieved 0 bytes");
+                Disconnect("Received 0 bytes");
                 return;
             }
 
@@ -225,7 +253,7 @@ namespace Hazel.Udp
             }
             catch (SocketException e)
             {
-                HandleDisconnect("A Socket exception occured while initiating a receive operation.");
+                Disconnect("Socket exception during receive: " + e.Message);
             }
             catch (ObjectDisposedException)
             {
@@ -242,45 +270,24 @@ namespace Hazel.Udp
             HandleReceive(msg, bytesReceived);
         }
 
-        /// <inheritdoc />
-        protected override void HandleDisconnect(string e)
-        {
-            if (State == ConnectionState.Connected)
-            {
-                State = ConnectionState.Disconnecting;
-
-                try
-                {
-                    InvokeDisconnected(e);
-                }
-                catch { }
-            }
-
-            Dispose();
-        }
-
         /// <inheritdoc />
         protected override void Dispose(bool disposing)
         {
             if (disposing)
             {
-                //Send disconnect message if we're not already disconnecting
-                if (State == ConnectionState.Connected)
+                if (this.state == ConnectionState.Connected
+                    || this.state == ConnectionState.Disconnecting)
                 {
-                    State = ConnectionState.NotConnected;
-                    try
-                    {
-                        SendDisconnect();
-                    }
-                    catch { }
+                    SendDisconnect();
+                    this.state = ConnectionState.NotConnected;
                 }
             }
 
-            if (socket != null)
+            if (this.socket != null)
             {
-                socket.Close();
-                socket.Dispose();
-                socket = null;
+                this.socket.Close();
+                this.socket.Dispose();
+                this.socket = null;
             }
 
             this.reliablePacketTimer.Dispose();
index 8227a6fd7d6890185a5560baec5b3ab3f8fd445b..a0d5f5a039a48ba1d68b5b02d0ded55f6e2eb072 100644 (file)
@@ -47,37 +47,29 @@ namespace Hazel.Udp
         /// </summary>
         Timer keepAliveTimer;
 
-        /// <summary>
-        ///     Lock for keep alive timer.
-        /// </summary>
-        Object keepAliveTimerLock = new Object();
-        
         /// <summary>
         ///     Starts the keepalive timer.
         /// </summary>
         void InitializeKeepAliveTimer()
         {
-            lock (keepAliveTimerLock)
-            {
-                keepAliveTimer = new Timer(
-                    (o) =>
+            keepAliveTimer = new Timer(
+                (o) =>
+                {
+                    try
                     {
-                        try
-                        {
-                            ReliableSend((byte)UdpSendOption.Ping);
-                            Interlocked.Increment(ref KeepAlivesSent);
-                        }
-                        catch
-                        {
-                            Trace.WriteLine("Keepalive packet failed to send.");
-                            DisposeKeepAliveTimer();
-                        }
-                    },
-                    null,
-                    keepAliveInterval,
-                    keepAliveInterval
-                );
-            }
+                        ReliableSend((byte)UdpSendOption.Ping);
+                        Interlocked.Increment(ref KeepAlivesSent);
+                    }
+                    catch
+                    {
+                        Trace.WriteLine("Keepalive packet failed to send.");
+                        DisposeKeepAliveTimer();
+                    }
+                },
+                null,
+                keepAliveInterval,
+                keepAliveInterval
+            );
         }
 
         /// <summary>
@@ -85,10 +77,11 @@ namespace Hazel.Udp
         /// </summary>
         void ResetKeepAliveTimer()
         {
-            lock (keepAliveTimerLock)
+            try
             {
                 keepAliveTimer.Change(keepAliveInterval, keepAliveInterval);
             }
+            catch { }
         }
 
         /// <summary>
@@ -96,13 +89,11 @@ namespace Hazel.Udp
         /// </summary>
         void DisposeKeepAliveTimer()
         {
-            lock (keepAliveTimerLock)
+            var timer = this.keepAliveTimer;
+            if (timer != null)
             {
-                if (keepAliveTimer != null)
-                {
-                    keepAliveTimer.Dispose();
-                    keepAliveTimer = null;
-                }
+                this.keepAliveTimer = null;
+                timer.Dispose();
             }
         }
     }
index 23be344af54b69f474c4c6b807ab3b144f39b52d..f5e89682e5f06a439b81989e7acde683a8a771ff 100644 (file)
@@ -28,6 +28,10 @@ namespace Hazel.Udp
         public int ResendTimeout { get { return resendTimeout; } set { resendTimeout = value; } }
         private volatile int resendTimeout = 0;
 
+        public volatile int ResendLimit = 0;
+
+        public volatile float ResendPingMultiplier = 3;
+
         /// <summary>
         ///     Holds the last ID allocated.
         /// </summary>
@@ -75,8 +79,7 @@ namespace Hazel.Udp
         ///     connection will be marked as disconnected and the <see cref="Connection.Disconnected">Disconnected</see> event
         ///     will be invoked.
         /// </remarks>
-        public int DisconnectTimeout { get { return disconnectTimeout; } set { disconnectTimeout = value; } }
-        private volatile int disconnectTimeout = 2500;
+        public volatile int DisconnectTimeout = 2500;
 
         /// <summary>
         ///     Class to hold packet data
@@ -98,13 +101,13 @@ namespace Hazel.Udp
             }
 
             public ushort Id;
-            public byte[] Data;
+            private byte[] Data;
+            private UdpConnection Connection;
+            private int Length;
 
-            public DateTime LastSend;
-            public int LastTimeout;
+            public volatile int NextTimeout;
             public volatile bool Acknowledged;
 
-            private Func<Packet, int> ResendAction;
             public Action AckCallback;
 
             public volatile int Retransmissions;
@@ -114,30 +117,65 @@ namespace Hazel.Udp
             {
             }
             
-            internal void Set(ushort id, byte[] data, Func<Packet, int> resendAction, int timeout, Action ackCallback)
+            internal void Set(ushort id, UdpConnection connection, byte[] data, int length, int timeout, Action ackCallback)
             {
                 this.Id = id;
                 this.Data = data;
+                this.Connection = connection;
+                this.Length = length;
 
                 this.Acknowledged = false;
-                this.LastSend = DateTime.Now;
-                this.LastTimeout = timeout;
-                this.ResendAction = resendAction;
-                AckCallback = ackCallback;
-                Retransmissions = 0;
+                this.NextTimeout = timeout;
+                this.AckCallback = ackCallback;
+                this.Retransmissions = 0;
 
-                Stopwatch.Restart();
+                this.Stopwatch.Restart();
             }
 
             // Packets resent
             public int Resend()
             {
-                var evt = this.ResendAction;
-                if (!this.Acknowledged)
+                var connection = this.Connection;
+                if (!this.Acknowledged && connection != null)
                 {
-                    if (evt != null)
+                    long lifetime = this.Stopwatch.ElapsedMilliseconds;
+                    if (lifetime >= connection.DisconnectTimeout)
                     {
-                        return evt(this);
+                        if (connection.reliableDataPacketsSent.TryRemove(this.Id, out Packet self))
+                        {
+                            connection.Disconnect($"Reliable packet {self.Id} was not ack'd after {lifetime}ms");
+
+                            self.Recycle();
+                        }
+
+                        return 0;
+                    }
+
+                    if (lifetime >= this.NextTimeout)
+                    {
+                        if (++this.Retransmissions > connection.ResendLimit)
+                        {
+                            if (connection.reliableDataPacketsSent.TryRemove(this.Id, out Packet self))
+                            {
+                                connection.Disconnect($"Reliable packet {self.Id} was not ack'd after {self.Retransmissions} resends");
+
+                                self.Recycle();
+                            }
+
+                            return 0;
+                        }
+
+                        this.NextTimeout = (int)Math.Min(this.NextTimeout * 3f, connection.DisconnectTimeout);
+                        try
+                        {
+                            connection.WriteBytesToConnection(this.Data, this.Length);
+                            return 1;
+                        }
+                        catch (InvalidOperationException)
+                        {
+                            //No longer connected
+                            connection.Disconnect("Could not resend data as connection is no longer connected");
+                        }
                     }
                 }
 
@@ -150,6 +188,7 @@ namespace Hazel.Udp
             public void Recycle()
             {
                 this.Acknowledged = true;
+                this.Connection = null;
 
                 PacketPool.PutObject(this);
             }
@@ -164,17 +203,14 @@ namespace Hazel.Udp
                 foreach (var kvp in this.reliableDataPacketsSent)
                 {
                     Packet pkt = kvp.Value;
-                    double timeSinceLast = (DateTime.Now - pkt.LastSend).TotalMilliseconds;
-                    if (timeSinceLast >= pkt.LastTimeout)
-                    {
+                    
                         try
                         {
                             output += pkt.Resend();
                         }
                         catch { }
-                    }
 
-                    minTimeout = Math.Min(pkt.LastTimeout, minTimeout);
+                    minTimeout = Math.Min(pkt.NextTimeout, minTimeout);
                 }
             }
 
@@ -197,59 +233,22 @@ namespace Hazel.Udp
 
             id = (ushort)Interlocked.Increment(ref lastIDAllocated);
 
-            if (!reliableDataPacketsSent.TryAdd(id, packet))
-            {
-                throw new Exception("That shouldn't be possible");
-            }
-
-            int timeout = resendTimeout > 0 ? resendTimeout : (int)Math.Max(50, Math.Min(AveragePingMs * 2, 1000));
-
-            //Write ID
             buffer[offset] = (byte)((id >> 8) & 0xFF);
             buffer[offset + 1] = (byte)id;
 
             packet.Set(
                 id,
+                this,
                 buffer,
-                (Packet p) =>
-                {
-                    // Callback for a previous packet
-                    if (p.Acknowledged) return 0;
-
-                    p.LastSend = DateTime.Now;
-                    p.LastTimeout = (int)Math.Min(p.LastTimeout * 1.5f, 3000);
-
-                    Packet self;
-                    if (p.Stopwatch.ElapsedMilliseconds > this.disconnectTimeout)
-                    {
-                        if (reliableDataPacketsSent.TryRemove(p.Id, out self))
-                        {
-                            HandleDisconnect($"Reliable packet {self.Id} was not ack'd after {self.Retransmissions} resends");
-
-                            self.Recycle();
-                        }
-
-                        return 0;
-                    }
-
-
-                    try
-                    {
-                        WriteBytesToConnection(p.Data, sendLength);
-                        p.Retransmissions++;
-                        return 1;
-                    }
-                    catch (InvalidOperationException)
-                    {
-                        //No longer connected
-                        HandleDisconnect("Could not resend data as connection is no longer connected");
-                    }
-
-                    return 0;
-                },
-                timeout,
+                sendLength,
+                resendTimeout > 0 ? resendTimeout : (int)Math.Max(300, Math.Min(AveragePingMs * this.ResendPingMultiplier, 2000)),
                 ackCallback
             );
+
+            if (!reliableDataPacketsSent.TryAdd(id, packet))
+            {
+                throw new Exception("That shouldn't be possible");
+            }
         }
 
         /// <summary>
index 3d6c720a5f1d3b46dd0debf05bd9f97275d45569..89e80e5a7b484aae855cf3ba20654e1d8675ce4a 100644 (file)
@@ -28,7 +28,13 @@ namespace Hazel.Udp
         /// </summary>
         /// <param name="bytes">The bytes to write.</param>
         protected abstract void WriteBytesToConnection(byte[] bytes, int length);
-        
+
+        /// <summary>
+        ///     Writes the given bytes to the connection synchronously.
+        /// </summary>
+        /// <param name="bytes">The bytes to write.</param>
+        protected abstract void WriteBytesToConnectionSync(byte[] bytes, int length);
+
         /// <inheritdoc/>
         public override void Send(MessageWriter msg)
         {
@@ -168,7 +174,7 @@ namespace Hazel.Udp
                     break;
 
                 case (byte)UdpSendOption.Disconnect:
-                    HandleDisconnect("The remote sent a disconnect request");
+                    Disconnect("The remote sent a disconnect request");
                     message.Recycle();
                     break;
                     
@@ -253,14 +259,40 @@ namespace Hazel.Udp
         ///     Called when the socket has been disconnected at the remote host.
         /// </summary>
         /// <param name="e">The exception if one was the cause.</param>
-        protected abstract void HandleDisconnect(string reason);
+        public override void Disconnect(string reason)
+        {
+            bool invoke = false;
+            lock (this)
+            {
+                if (this.state == ConnectionState.Connected)
+                {
+                    this.state = ConnectionState.Disconnecting;
+                    invoke = true;
+                }
+            }
+
+            if (invoke)
+            {
+                try
+                {
+                    InvokeDisconnected(reason);
+                }
+                catch { }
+            }
+
+            this.Dispose();
+        }
 
         /// <summary>
         ///     Sends a disconnect message to the end point.
         /// </summary>
-        public override void SendDisconnect()
+        protected override void SendDisconnect()
         {
-            WriteBytesToConnection(new byte[] { (byte)UdpSendOption.Disconnect }, 1);
+            try
+            {
+                WriteBytesToConnectionSync(new byte[] { (byte)UdpSendOption.Disconnect }, 1);
+            }
+            catch { }
         }
 
         /// <inheritdoc/>
index 0c2239aa87cafdf5fc4e39f0133b7e2b10e3b111..4c2584d9241733c39bfd4dd7460e3e4a2cfd483b 100644 (file)
@@ -26,8 +26,10 @@ namespace Hazel.Udp
         /// <summary>
         ///     The socket listening for connections.
         /// </summary>
-        Socket listener;
-        
+        Socket socket;
+
+        private Action<string> Logger;
+
         Timer reliablePacketTimer;
 
         /// <summary>
@@ -41,20 +43,21 @@ namespace Hazel.Udp
         ///     Creates a new UdpConnectionListener for the given <see cref="IPAddress"/>, port and <see cref="IPMode"/>.
         /// </summary>
         /// <param name="endPoint">The endpoint to listen on.</param>
-        public UdpConnectionListener(NetworkEndPoint endPoint)
+        public UdpConnectionListener(NetworkEndPoint endPoint, Action<string> logger = null)
         {
+            this.Logger = logger;
             this.EndPoint = endPoint.EndPoint;
             this.IPMode = endPoint.IPMode;
 
             if (endPoint.IPMode == IPMode.IPv4)
-                this.listener = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
+                this.socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
             else
             {
                 if (!Socket.OSSupportsIPv6)
                     throw new HazelException("IPV6 not supported!");
 
-                this.listener = new Socket(AddressFamily.InterNetworkV6, SocketType.Dgram, ProtocolType.Udp);
-                this.listener.SetSocketOption(SocketOptionLevel.IPv6, (SocketOptionName)27, false);
+                this.socket = new Socket(AddressFamily.InterNetworkV6, SocketType.Dgram, ProtocolType.Udp);
+                this.socket.SetSocketOption(SocketOptionLevel.IPv6, (SocketOptionName)27, false);
             }
 
             reliablePacketTimer = new Timer(ManageReliablePackets, null, 100, Timeout.Infinite);
@@ -77,7 +80,7 @@ namespace Hazel.Udp
             foreach (var kvp in this.allConnections)
             {
                 var sock = kvp.Value;
-                PacketsResent += sock.ManageReliablePackets(state);
+                Interlocked.Add(ref PacketsResent, sock.ManageReliablePackets(state));
                 KeepAlives += Interlocked.Exchange(ref sock.KeepAlivesSent, 0);
                 DuplicateRecieves += Interlocked.Exchange(ref sock.DuplicateRecieves, 0);
             }
@@ -92,7 +95,7 @@ namespace Hazel.Udp
         {
             try
             {
-                listener.Bind(EndPoint);
+                socket.Bind(EndPoint);
             }
             catch (SocketException e)
             {
@@ -114,7 +117,7 @@ namespace Hazel.Udp
             {
                 message = MessageReader.GetSized(BufferSize);
 
-                listener.BeginReceiveFrom(message.Buffer, 0, message.Buffer.Length, SocketFlags.None, ref remoteEP, ReadCallback, message);
+                socket.BeginReceiveFrom(message.Buffer, 0, message.Buffer.Length, SocketFlags.None, ref remoteEP, ReadCallback, message);
                 Interlocked.Increment(ref ActiveListeners);
             }
             catch (ObjectDisposedException)
@@ -138,113 +141,133 @@ namespace Hazel.Udp
         
         public int ActiveListeners;
         public int ActiveCallbacks;
+
         void ReadCallback(IAsyncResult result)
         {
-            var message = (MessageReader)result.AsyncState;
             Interlocked.Increment(ref ActiveCallbacks);
+            Interlocked.Decrement(ref ActiveListeners);
 
-            int bytesReceived;
-            EndPoint remoteEndPoint = new IPEndPoint(IPMode == IPMode.IPv4 ? IPAddress.Any : IPAddress.IPv6Any, 0);
-
-            //End the receive operation
             try
             {
-                Interlocked.Decrement(ref ActiveListeners);
-                bytesReceived = listener.EndReceiveFrom(result, ref remoteEndPoint);
-                Interlocked.Add(ref BytesReceived, bytesReceived);
-
-                message.Offset = 0;
-                message.Length = bytesReceived;
-            }
-            catch (NullReferenceException)
-            {
-                return;
-            }
-            catch (ObjectDisposedException)
-            {
-                //If the socket's been disposed then we can just end there.
-                return;
-            }
-            catch (SocketException)
-            {
-                // Client no longer reachable, pretend it didn't happen
-                // TODO should this not inform the connection this client is lost???
+                var message = (MessageReader)result.AsyncState;
+                int bytesReceived;
+                EndPoint remoteEndPoint = new IPEndPoint(IPMode == IPMode.IPv4 ? IPAddress.Any : IPAddress.IPv6Any, 0);
 
-                // This thread suggests the IP is not passed out from WinSoc so maybe not possible
-                // http://stackoverflow.com/questions/2576926/python-socket-error-on-udp-data-receive-10054
-                message.Recycle();
+                //End the receive operation
+                try
+                {
+                    bytesReceived = socket.EndReceiveFrom(result, ref remoteEndPoint);
+                    Interlocked.Add(ref BytesReceived, bytesReceived);
 
-                UdpServerConnection dead;
-                if (this.allConnections.TryRemove(remoteEndPoint, out dead))
+                    message.Offset = 0;
+                    message.Length = bytesReceived;
+                }
+                catch (NullReferenceException)
+                {
+                    return;
+                }
+                catch (ObjectDisposedException)
                 {
-                    dead.Dispose();
+                    //If the socket's been disposed then we can just end there.
+                    return;
                 }
+                catch (SocketException)
+                {
+                    // Client no longer reachable, pretend it didn't happen
+                    // TODO should this not inform the connection this client is lost???
 
-                StartListeningForData();
-                return;
-            }
+                    // This thread suggests the IP is not passed out from WinSoc so maybe not possible
+                    // http://stackoverflow.com/questions/2576926/python-socket-error-on-udp-data-receive-10054
+                    message.Recycle();
 
-            // Exit if no bytes read, we've closed.
-            if (bytesReceived == 0)
-            {
-                message.Recycle();
-                return;
-            }
+                    UdpServerConnection dead;
+                    if (this.allConnections.TryRemove(remoteEndPoint, out dead))
+                    {
+                        dead.Dispose();
+                    }
 
-            //Begin receiving again
-            StartListeningForData();
+                    StartListeningForData();
+                    return;
+                }
 
-            bool aware = true;
-            bool hasHelloByte = message.Buffer[0] == (byte)UdpSendOption.Hello;
-            bool isHello = hasHelloByte && message.Length >= MinConnectionLength;
+                // Exit if no bytes read, we've closed.
+                if (bytesReceived == 0)
+                {
+                    message.Recycle();
+                    return;
+                }
 
-            //If we're aware of this connection use the one already
-            //If this is a new client then connect with them!
-            UdpServerConnection connection;
-            if (!this.allConnections.TryGetValue(remoteEndPoint, out connection))
-            {
-                lock (this.allConnections)
+                //Begin receiving again
+                StartListeningForData();
+
+                bool aware = true;
+                bool hasHelloByte = message.Buffer[0] == (byte)UdpSendOption.Hello;
+                bool isHello = hasHelloByte && message.Length >= MinConnectionLength;
+
+                //If we're aware of this connection use the one already
+                //If this is a new client then connect with them!
+                UdpServerConnection connection;
+                if (!this.allConnections.TryGetValue(remoteEndPoint, out connection))
                 {
-                    aware = this.allConnections.TryGetValue(remoteEndPoint, out connection);
-                    if (!aware)
+                    //Check for malformed connection attempts
+                    if (!isHello)
                     {
-                        //Check for malformed connection attempts
-                        if (!isHello)
-                        {
-                            Interlocked.Decrement(ref ActiveCallbacks);
-                            message.Recycle();
-                            return;
-                        }
+                        message.Recycle();
+                        return;
+                    }
 
-                        connection = new UdpServerConnection(this, remoteEndPoint, this.IPMode);
-                        if (!this.allConnections.TryAdd(remoteEndPoint, connection))
+                    lock (this.allConnections)
+                    {
+                        aware = this.allConnections.TryGetValue(remoteEndPoint, out connection);
+                        if (!aware)
                         {
-                            throw new Exception();
+                            connection = new UdpServerConnection(this, remoteEndPoint, this.IPMode);
+                            if (!this.allConnections.TryAdd(remoteEndPoint, connection))
+                            {
+                                throw new Exception();
+                            }
                         }
                     }
                 }
-            }
 
-            //Inform the connection of the buffer (new connections need to send an ack back to client)
-            connection.HandleReceive(message, bytesReceived);
+                var stopwatch = System.Diagnostics.Stopwatch.StartNew();
+                try
+                {
+                    //Inform the connection of the buffer (new connections need to send an ack back to client)
+                    connection.HandleReceive(message, bytesReceived);
+                }
+                finally
+                {
+                    var el = stopwatch.ElapsedMilliseconds;
+                    if (el > 5)
+                    {
+                        this.Logger?.Invoke($"Long Packet {el}ms = {string.Join(" ", message.Buffer.Take(bytesReceived))}");
+                    }
+                }
 
-            //If it's a new connection invoke the NewConnection event.
-            if (!aware)
-            {
-                // Skip header and hello byte;
-                message.Offset = 4;
-                message.Length = bytesReceived - 4;
-                message.Position = 0;
-                InvokeNewConnection(message, connection);
+                //If it's a new connection invoke the NewConnection event.
+                if (!aware)
+                {
+                    // Skip header and hello byte;
+                    message.Offset = 4;
+                    message.Length = bytesReceived - 4;
+                    message.Position = 0;
+                    InvokeNewConnection(message, connection);
+                }
+                else if (isHello || (!isHello && hasHelloByte))
+                {
+                    message.Recycle();
+                }
             }
-            else if (isHello || (!isHello && hasHelloByte))
+            finally
             {
-                message.Recycle();
+                Interlocked.Decrement(ref ActiveCallbacks);
             }
-
-            Interlocked.Decrement(ref ActiveCallbacks);
         }
 
+        public int TestDropRate = -1;
+        private int dropCounter = 0;
+
         /// <summary>
         ///     Sends data from the listener socket.
         /// </summary>
@@ -255,9 +278,17 @@ namespace Hazel.Udp
             if (length > bytes.Length) return;
             Interlocked.Add(ref BytesSent, length);
 
+            if (TestDropRate > 0)
+            {
+                if (Interlocked.Increment(ref dropCounter) % TestDropRate == 0)
+                {
+                    return;
+                }
+            }
+
             try
             {
-                listener.BeginSendTo(
+                socket.BeginSendTo(
                     bytes,
                     0,
                     length,
@@ -267,7 +298,7 @@ namespace Hazel.Udp
                     {
                         try
                         {
-                            listener.EndSendTo(result);
+                            socket.EndSendTo(result);
                         }
                         catch { }
                     },
@@ -294,7 +325,7 @@ namespace Hazel.Udp
         {
             try
             {
-                listener.SendTo(
+                socket.SendTo(
                     bytes,
                     0,
                     length,
@@ -330,11 +361,11 @@ namespace Hazel.Udp
                 kvp.Value.Dispose();
             }
 
-            if (listener != null)
+            if (this.socket != null)
             {
-                listener.Close();
-                this.listener.Dispose();
-                this.listener = null;
+                this.socket.Close();
+                this.socket.Dispose();
+                this.socket = null;
             }
 
             this.reliablePacketTimer.Dispose();
index 27f593caaa7c7fa8fb6f1bc14bf38ea20f2a4188..1b8847ecdc164a51d9e2a736e56f9d1a4e1f57e1 100644 (file)
@@ -25,7 +25,7 @@ namespace Hazel.Udp
         /// <summary>
         ///     Lock object for the writing to the state of the connection.
         /// </summary>
-        Object stateLock = new Object();
+        private ReaderWriterLockSlim stateLock = new ReaderWriterLockSlim();
 
         /// <summary>
         ///     Creates a UdpConnection for the virtual connection to the endpoint.
@@ -49,15 +49,22 @@ namespace Hazel.Udp
         {
             InvokeDataSentRaw(bytes, length);
 
-            lock (stateLock)
-            {
-                if (State != ConnectionState.Connected)
-                    throw new InvalidOperationException("Could not send data as this Connection is not connected. Did you disconnect?");
-            }
+            if (State != ConnectionState.Connected)
+                throw new InvalidOperationException("Could not send data: Not connected.");
 
             Listener.SendData(bytes, length, RemoteEndPoint);
         }
 
+        /// <inheritdoc />
+        protected override void WriteBytesToConnectionSync(byte[] bytes, int length)
+        {
+            InvokeDataSentRaw(bytes, length);
+
+            // No throw: As an internal interface, I want to try sending bytes whenever the I feel like it.
+
+            Listener.SendDataSync(bytes, length, RemoteEndPoint);
+        }
+
         /// <inheritdoc />
         /// <remarks>
         ///     This will always throw a HazelException.
@@ -75,54 +82,19 @@ namespace Hazel.Udp
         {
             throw new HazelException("Cannot manually connect a UdpServerConnection, did you mean to use UdpClientConnection?");
         }
-
-        /// <inheritdoc />
-        protected override void HandleDisconnect(string reason)
-        {
-            bool invoke = false;
-
-            lock (stateLock)
-            {
-                //Only invoke the disconnected event if we're not already disconnecting
-                if (State == ConnectionState.Connected)
-                {
-                    State = ConnectionState.Disconnecting;
-                    invoke = true;
-                }
-            }
-
-            //Invoke event outide lock if need be
-            if (invoke)
-            {
-                try
-                {
-                    InvokeDisconnected(reason);
-                }
-                catch { }
-
-                Dispose();
-            }
-        }
-
-        /// <inheritdoc />
+        
         protected override void Dispose(bool disposing)
         {
-            //Here we just need to inform the listener we no longer need data.
+            Listener.RemoveConnectionTo(RemoteEndPoint);
+
             if (disposing)
             {
-                // Send disconnect message if we're not already disconnecting
-                if (this.state == ConnectionState.Connected)
+                if (this.state == ConnectionState.Connected
+                    || this.state == ConnectionState.Disconnecting)
                 {
-                    try
-                    {
-                        SendDisconnect();
-                    }
-                    catch { }
-                    this.state = ConnectionState.Disconnecting;
+                    SendDisconnect();
+                    this.state = ConnectionState.NotConnected;
                 }
-
-                Listener.RemoveConnectionTo(RemoteEndPoint);
-
             }
 
             base.Dispose(disposing);