]> git.deb.at Git - rhonda/impostor.hazel.git/commitdiff
Lots of code clean up, fixed an issue were a latent connection could cause packet...
authorForest <chocozilla@gmail.com>
Thu, 25 Jul 2019 21:47:08 +0000 (14:47 -0700)
committerForest <chocozilla@gmail.com>
Thu, 25 Jul 2019 21:47:08 +0000 (14:47 -0700)
13 files changed:
Hazel/Connection.cs
Hazel/ConnectionListener.cs
Hazel/DisconnectedEventArgs.cs
Hazel/MessageWriter.cs
Hazel/NetworkConnection.cs
Hazel/ObjectPool.cs
Hazel/Udp/SendOptionInternal.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 3615c51064085c0829c1285b44f9599e2a09309e..50daf44c461e17635dc5988dd9b424d3231a7111 100644 (file)
@@ -94,22 +94,8 @@ namespace Hazel
         ///     The state of this connection.
         /// </summary>
         /// <remarks>
-        ///     <para>
-        ///         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="Dispose"/> sets them to 
-        ///         <see cref="ConnectionState.Disconnecting"/> and then the sequence repeats back to
-        ///         <see cref="ConnectionState.NotConnected"/> once disconnection is complete.
-        ///     </para>
-        ///     <para>
-        ///         Data can only be sent while in <see cref="ConnectionState.Connected"/> and all attempts to send data when
-        ///         in any other state will throw an InvalidOperationException.
-        ///     </para>
-        ///     <para>
-        ///         All implementers should be aware that when this is set to <see cref="ConnectionState.Connected"/> it will
-        ///         release all threads that are blocked on <see cref="WaitOnConnect"/>.
-        ///     </para>
+        ///     All implementers should be aware that when this is set to ConnectionState.Connected it will
+        ///     release all threads that are blocked on <see cref="WaitOnConnect"/>.
         /// </remarks>
         public ConnectionState State
         {
@@ -179,27 +165,19 @@ namespace Hazel
         
         /// <summary>
         ///     Connects the connection to a server and begins listening.
+        ///     This method blocks and may thrown if there is a problem connecting.
         /// </summary>
         /// <param name="bytes">The bytes of data to send in the handshake.</param>
         /// <param name="timeout">The number of milliseconds to wait before giving up on the connect attempt.</param>
-        /// <remarks>
-        ///     Calling Connect makes the connection attempt to connect to the end point that's specified in the 
-        ///     constructor. This method will block until the connection attempt completes and will throw a 
-        ///     <see cref="HazelException"/> if there is a problem connecting.
-        /// </remarks>
         public abstract void Connect(byte[] bytes = null, int timeout = 5000);
 
 
         /// <summary>
         ///     Connects the connection to a server and begins listening.
+        ///     This method does not block.
         /// </summary>
         /// <param name="bytes">The bytes of data to send in the handshake.</param>
         /// <param name="timeout">The number of milliseconds to wait before giving up on the connect attempt.</param>
-        /// <remarks>
-        ///     Calling Connect makes the connection attempt to connect to the end point that's specified in the 
-        ///     constructor. This method will block until the connection attempt completes and will throw a 
-        ///     <see cref="HazelException"/> if there is a problem connecting.
-        /// </remarks>
         public abstract void ConnectAsync(byte[] bytes = null, int timeout = 5000);
 
         /// <summary>
@@ -229,11 +207,11 @@ namespace Hazel
         /// <summary>
         ///     Invokes the Disconnected event.
         /// </summary>
-        /// <param name="e">The exception, if any, that occured to cause this.</param>
+        /// <param name="e">The exception, if any, that occurred to cause this.</param>
         /// <param name="reader">Extra disconnect data</param>
         /// <remarks>
         ///     Invokes the <see cref="Disconnected"/> event to alert subscribres this connection has been disconnected either 
-        ///     by the end point or because an error occured. If an error occured the error should be passed in in order to 
+        ///     by the end point or because an error occurred. If an error occurred the error should be passed in in order to 
         ///     pass to the subscribers, otherwise null can be passed in.
         /// </remarks>
         protected void InvokeDisconnected(string e, MessageReader reader)
@@ -251,11 +229,6 @@ namespace Hazel
         ///     Blocks until the Connection is connected.
         /// </summary>
         /// <param name="timeout">The number of milliseconds to wait before timing out.</param>
-        /// <remarks>
-        ///     This is a helper method for waiting until the connection is connected. It will block until the 
-        ///     <see cref="State"/> property is set to <see cref="ConnectionState.Connected"/> allowing the main thread to 
-        ///     wait until specific data is received etc. before returning to the user's code.
-        /// </remarks>
         protected bool WaitOnConnect(int timeout)
         {
             return connectWaitLock.WaitOne(timeout);
@@ -265,7 +238,7 @@ namespace Hazel
         /// 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>
-        public abstract void Disconnect(string reason, MessageWriter writer = null, bool fireEvent = true);
+        public abstract void Disconnect(string reason, MessageWriter writer = null);
         
         /// <summary>
         ///     Disposes of this NetworkConnection.
index 9d9c66413d3dfbbf2de6deaff6da8bbf0903f9f0..b4b852fef2ae7ed2ac10cb848f36ca6a86ed6b9e 100644 (file)
@@ -8,9 +8,7 @@ namespace Hazel
     /// <remarks>
     ///     <para>
     ///         ConnectionListeners are server side objects that listen for clients and create matching server side connections 
-    ///         for each client in a similar way to TCP does. These connections should already have a 
-    ///         <see cref="Connection.State">State</see> of <see cref="ConnectionState.Connected"/> and so should be ready for 
-    ///         comunication immediately.
+    ///         for each client in a similar way to TCP does. These connections should be ready for communication immediately.
     ///     </para>
     ///     <para>
     ///         Each time a client connects the <see cref="NewConnection"/> event will be invoked to alert all subscribers to
index bbecf81fbdf9159bf9ebfe2e042ac9ffd3f4a98c..a7fb05cab8967b554ab15dd995d7ac42c119ea4a 100644 (file)
@@ -1,34 +1,18 @@
 using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
 
 namespace Hazel
 {
-    /// <summary>
-    ///     Event arguments for the <see cref="Connection.Disconnected"/> event.
-    /// </summary>
-    /// <remarks>
-    ///     <para>
-    ///         This contains information about the cause of a disconnection and is passed to subscribers of the
-    ///         <see cref="Connection.Disconnected"/> event.
-    ///     </para>
-    ///     <include file="DocInclude/common.xml" path="docs/item[@name='Recyclable']/*" />
-    /// </remarks>
-    /// <threadsafety static="true" instance="true"/>
     public class DisconnectedEventArgs : EventArgs
     {
         /// <summary>
-        ///     The exception, if any, that caused the disconnect.
+        /// Optional disconnect reason. May be null.
         /// </summary>
-        /// <remarks>
-        ///     If the disconnection was caused because of an exception occuring (for exemple a 
-        ///     <see cref="System.Net.Sockets.SocketException"/> on network based connections) this will contain the error 
-        ///     that caused it or a <see cref="HazelException"/> with the details of the exception, if the disconnection 
-        ///     wasn't caused by an error then this will contain null.
-        /// </remarks>
         public readonly string Reason;
 
+        /// <summary>
+        /// Optional data sent with a disconnect message. May be null. 
+        /// You must not recycle this. If you need the message outside of a callback, you should copy it.
+        /// </summary>
         public readonly MessageReader Message;
 
         public DisconnectedEventArgs(string reason, MessageReader message)
index 95b01436f6b27cf780e3c15bea48bdf2d827e5a2..73a6efdaa0b8255e044d5f72ad2d44373482bddf 100644 (file)
@@ -112,6 +112,7 @@ namespace Hazel
             this.Buffer[0] = (byte)sendOption;
             switch (sendOption)
             {
+                default:
                 case SendOption.None:
                     this.Length = this.Position = 1;
                     break;
@@ -234,6 +235,12 @@ namespace Hazel
             this.Write(bytes, length);
         }
 
+        public void WriteBytesAndSize(byte[] bytes, int offset, int length)
+        {
+            this.WritePacked((uint)length);
+            this.Write(bytes, offset, length);
+        }
+
         public void Write(byte[] bytes)
         {
             Array.Copy(bytes, 0, this.Buffer, this.Position, bytes.Length);
@@ -241,6 +248,13 @@ namespace Hazel
             if (this.Position > this.Length) this.Length = this.Position;
         }
 
+        public void Write(byte[] bytes, int offset, int length)
+        {
+            Array.Copy(bytes, offset, this.Buffer, this.Position, length);
+            this.Position += length;
+            if (this.Position > this.Length) this.Length = this.Position;
+        }
+
         public void Write(byte[] bytes, int length)
         {
             Array.Copy(bytes, 0, this.Buffer, this.Position, length);
@@ -271,6 +285,25 @@ namespace Hazel
         }
         #endregion
 
+        public void Write(MessageWriter msg, bool includeHeader)
+        {
+            int offset = 0;
+            if (!includeHeader)
+            {
+                switch (msg.SendOption)
+                {
+                    case SendOption.None:
+                        offset = 1;
+                        break;
+                    case SendOption.Reliable:
+                        offset = 3;
+                        break;
+                }
+            }
+
+            this.Write(msg.Buffer, offset, msg.Length - offset);
+        }
+
         public unsafe static bool IsLittleEndian()
         {
             byte b;
index f670d115ce54f7bd7698c7ec3a0235a7e97b545d..eb63fd2ae2935f0dd448926dcf8a6a44707477c6 100644 (file)
@@ -60,9 +60,9 @@ namespace Hazel
         /// <summary>
         ///     Called when the socket has been disconnected locally.
         /// </summary>
-        public override void Disconnect(string reason, MessageWriter writer = null, bool fireEvent = true)
+        public override void Disconnect(string reason, MessageWriter writer = null)
         {
-            if (this.SendDisconnect(writer) && fireEvent)
+            if (this.SendDisconnect(writer))
             {
                 try
                 {
index d508fbad79b4931e6ab71c0aa5ede4bd6d8b3f1c..889969925b0056f200e0d734d99664f05af36c7b 100644 (file)
@@ -43,8 +43,7 @@ namespace Hazel
         /// <returns>An instance of T.</returns>
         internal T GetObject()
         {
-            T item;
-            if (!pool.TryTake(out item))
+            if (!pool.TryTake(out T item))
             {
                 Interlocked.Increment(ref numberCreated);
                 item = objectFactory.Invoke();
@@ -73,5 +72,10 @@ namespace Hazel
                 throw new Exception("Duplicate add " + typeof(T).Name);
             }
         }
+
+        public bool IsObjectInUse(T item)
+        {
+            return inuse.ContainsKey(item);
+        }
     }
 }
index 397334f144a010a6270f575d35850eb1d022db07..74786d8238db558b7cd01618fe4caeff74adaf96 100644 (file)
@@ -9,7 +9,7 @@ namespace Hazel.Udp
     /// <summary>
     ///     Extra internal states for SendOption enumeration when using UDP.
     /// </summary>
-    enum UdpSendOption : byte
+    public enum UdpSendOption : byte
     {
         /// <summary>
         ///     Hello message for initiating communication.
index fd4145b5899087dfea09429aa5143fadfed88f6d..c02cd31bc6b0f7d37eec3777bee6229a7ca5a702 100644 (file)
@@ -97,7 +97,7 @@ namespace Hazel.Udp
             }
             catch (SocketException ex)
             {
-                Disconnect("Could not send data as a SocketException occured: " + ex.Message);
+                Disconnect("Could not send data as a SocketException occurred: " + ex.Message);
             }
         }
 
@@ -114,7 +114,7 @@ namespace Hazel.Udp
             }
             catch (SocketException ex)
             {
-                Disconnect("Could not send data as a SocketException occured: " + ex.Message);
+                Disconnect("Could not send data as a SocketException occurred: " + ex.Message);
             }
         }
 
@@ -149,7 +149,7 @@ namespace Hazel.Udp
             catch (SocketException e)
             {
                 this.State = ConnectionState.NotConnected;
-                throw new HazelException("A socket exception occured while binding to the port.", e);
+                throw new HazelException("A SocketException occurred while binding to the port.", e);
             }
 
             try
@@ -166,12 +166,16 @@ namespace Hazel.Udp
             catch (SocketException e)
             {
                 Dispose();
-                throw new HazelException("A Socket exception occured while initiating a receive operation.", e);
+                throw new HazelException("A SocketException occurred while initiating a receive operation.", e);
             }
 
             // Write bytes to the server to tell it hi (and to punch a hole in our NAT, if present)
             // When acknowledged set the state to connected
-            SendHello(bytes, () => { this.State = ConnectionState.Connected; });
+            SendHello(bytes, () =>
+            {
+                this.State = ConnectionState.Connected;
+                this.InitializeKeepAliveTimer();
+            });
         }
 
         /// <summary>
@@ -202,20 +206,15 @@ namespace Hazel.Udp
             {
                 msg.Length = socket.EndReceive(result);
             }
-            catch (NullReferenceException)
-            {
-                msg.Recycle();
-                return;
-            }
-            catch (ObjectDisposedException)
+            catch (SocketException e)
             {
                 msg.Recycle();
+                Disconnect("Socket exception while reading data: " + e.Message);
                 return;
             }
-            catch (SocketException e)
+            catch (Exception)
             {
                 msg.Recycle();
-                Disconnect("Socket exception while reading data: " + e.Message);
                 return;
             }
 
@@ -267,7 +266,7 @@ namespace Hazel.Udp
         {
             lock (this)
             {
-                if (this._state != ConnectionState.Connected) return false;
+                if (this._state == ConnectionState.NotConnected) return false;
                 this._state = ConnectionState.NotConnected;
             }
 
@@ -302,14 +301,9 @@ namespace Hazel.Udp
                 SendDisconnect();
             }
 
-            if (this.socket != null)
-            {
-                try { this.socket.Shutdown(SocketShutdown.Both); } catch { }
-                try { this.socket.Close(); } catch { }
-                try { this.socket.Dispose(); } catch { }
-
-                this.socket = null;
-            }
+            try { this.socket.Shutdown(SocketShutdown.Both); } catch { }
+            try { this.socket.Close(); } catch { }
+            try { this.socket.Dispose(); } catch { }
 
             this.reliablePacketTimer.Dispose();
 
index 20d1facebd06a09458bf0539b6cbd4eb98db786d..71babf07399a7a409d5f31ad68d948c9e714298d 100644 (file)
@@ -55,43 +55,41 @@ namespace Hazel.Udp
             set
             {
                 keepAliveInterval = value;
-
-                //Update timer
                 ResetKeepAliveTimer();
             }
         }
-        int keepAliveInterval = 1500;
+        private int keepAliveInterval = 1500;
 
         public int MissingPingsUntilDisconnect { get; set; } = 6;
-        int pingsSinceAck = 0;
+        private volatile int pingsSinceAck = 0;
 
         /// <summary>
         ///     The timer creating keepalive pulses.
         /// </summary>
-        Timer keepAliveTimer;
+        private Timer keepAliveTimer;
 
         /// <summary>
         ///     Starts the keepalive timer.
         /// </summary>
-        void InitializeKeepAliveTimer()
+        protected void InitializeKeepAliveTimer()
         {
             keepAliveTimer = new Timer(
                 (o) =>
                 {
                     if (this.pingsSinceAck >= this.MissingPingsUntilDisconnect)
                     {
+                        this.DisposeKeepAliveTimer();
                         this.Disconnect($"Sent {this.pingsSinceAck} pings that remote has not responded to.");
                         return;
                     }
 
                     try
                     {
-                        SendPing();
                         this.pingsSinceAck++;
+                        SendPing();
                     }
                     catch
                     {
-                        DisposeKeepAliveTimer();
                     }
                 },
                 null,
@@ -105,7 +103,7 @@ namespace Hazel.Udp
         // An unacked ping should never be the sole cause of a disconnect.
         // Rather, the responses will reset our pingsSinceAck, enough unacked 
         // pings should cause a disconnect.
-        void SendPing()
+        private void SendPing()
         {
             ushort id = (ushort)Interlocked.Increment(ref lastIDAllocated);
 
@@ -134,7 +132,7 @@ namespace Hazel.Udp
         /// <summary>
         ///     Resets the keepalive timer to zero.
         /// </summary>
-        void ResetKeepAliveTimer()
+        private void ResetKeepAliveTimer()
         {
             try
             {
@@ -146,13 +144,11 @@ namespace Hazel.Udp
         /// <summary>
         ///     Disposes of the keep alive timer.
         /// </summary>
-        void DisposeKeepAliveTimer()
+        private void DisposeKeepAliveTimer()
         {
-            var timer = this.keepAliveTimer;
-            if (timer != null)
+            if (this.keepAliveTimer != null)
             {
-                this.keepAliveTimer = null;
-                timer.Dispose();
+                this.keepAliveTimer.Dispose();
             }
 
             foreach (var kvp in activePingPackets)
index b18c8c81e318c196353d7cfbd37f8b881888f75e..6f7e54efee01cc9e004d88ff3fca6872cb049dfc 100644 (file)
@@ -72,7 +72,7 @@ namespace Hazel.Udp
         ///     This returns the average ping for a one-way trip as calculated from the reliable packets that have been sent 
         ///     and acknowledged by the endpoint.
         /// </remarks>
-        public float AveragePingMs = 200;
+        public float AveragePingMs = 500;
 
         /// <summary>
         ///     The maximum times a message should be resent before marking the endpoint as disconnected.
@@ -171,7 +171,7 @@ namespace Hazel.Udp
                             return 0;
                         }
 
-                        this.NextTimeout = (int)Math.Min(this.NextTimeout * connection.ResendPingMultiplier, 1500);
+                        this.NextTimeout += (int)Math.Min(this.NextTimeout * connection.ResendPingMultiplier, 500);
                         try
                         {
                             connection.WriteBytesToConnection(this.Data, this.Length);
@@ -225,7 +225,7 @@ namespace Hazel.Udp
         /// <param name="buffer">The buffer to attach to.</param>
         /// <param name="offset">The offset to attach at.</param>
         /// <param name="ackCallback">The callback to make once the packet has been acknowledged.</param>
-        void AttachReliableID(byte[] buffer, int offset, int sendLength, Action ackCallback = null)
+        private void AttachReliableID(byte[] buffer, int offset, int sendLength, Action ackCallback = null)
         {
             ushort id = (ushort)Interlocked.Increment(ref lastIDAllocated);
 
@@ -260,7 +260,7 @@ namespace Hazel.Udp
         /// <param name="sendOption"></param>
         /// <param name="data">The byte array to write to.</param>
         /// <param name="ackCallback">The callback to make once the packet has been acknowledged.</param>
-        void ReliableSend(byte sendOption, byte[] data, Action ackCallback = null)
+        private void ReliableSend(byte sendOption, byte[] data, Action ackCallback = null)
         {
             this.ReliableSend(sendOption, data, 0, data.Length, ackCallback);
         }
@@ -273,7 +273,7 @@ namespace Hazel.Udp
         /// <param name="offset"></param>
         /// <param name="length"></param>
         /// <param name="ackCallback">The callback to make once the packet has been acknowledged.</param>
-        void ReliableSend(byte sendOption, byte[] data, int offset, int length, Action ackCallback = null)
+        private void ReliableSend(byte sendOption, byte[] data, int offset, int length, Action ackCallback = null)
         {
             //Inform keepalive not to send for a while
             ResetKeepAliveTimer();
@@ -299,7 +299,7 @@ namespace Hazel.Udp
         ///     Handles a reliable message being received and invokes the data event.
         /// </summary>
         /// <param name="message">The buffer received.</param>
-        void ReliableMessageReceive(MessageReader message, int bytesReceived)
+        private void ReliableMessageReceive(MessageReader message, int bytesReceived)
         {
             ushort id;
             if (ProcessReliableReceive(message.Buffer, 1, out id))
@@ -320,7 +320,7 @@ namespace Hazel.Udp
         /// <param name="bytes">The buffer containing the data.</param>
         /// <param name="offset">The offset of the reliable header.</param>
         /// <returns>Whether the packet was a new packet or not.</returns>
-        bool ProcessReliableReceive(byte[] bytes, int offset, out ushort id)
+        private bool ProcessReliableReceive(byte[] bytes, int offset, out ushort id)
         {
             byte b1 = bytes[offset];
             byte b2 = bytes[offset + 1];
@@ -400,7 +400,7 @@ namespace Hazel.Udp
         ///     Handles acknowledgement packets to us.
         /// </summary>
         /// <param name="bytes">The buffer containing the data.</param>
-        void AcknowledgementMessageReceive(byte[] bytes)
+        private void AcknowledgementMessageReceive(byte[] bytes)
         {
             this.pingsSinceAck = 0;
 
@@ -440,7 +440,7 @@ namespace Hazel.Udp
         /// </summary>
         /// <param name="byte1">The first identification byte.</param>
         /// <param name="byte2">The second identification byte.</param>
-        internal void SendAck(byte byte1, byte byte2)
+        private void SendAck(byte byte1, byte byte2)
         {
             byte[] bytes = new byte[]
             {
@@ -458,7 +458,7 @@ namespace Hazel.Udp
             catch (InvalidOperationException) { }
         }
 
-        void DisposeReliablePackets()
+        private void DisposeReliablePackets()
         {
             foreach (var kvp in reliableDataPacketsSent)
             {
index 5cc56ebf43e94da1a04ca499109cbfce3094bd8f..014a61e402e22e972c1823efcea4fde3fb7c6fa0 100644 (file)
@@ -10,14 +10,6 @@ namespace Hazel.Udp
     {
         protected static readonly byte[] EmptyDisconnectBytes = new byte[] { (byte)UdpSendOption.Disconnect };
 
-        /// <summary>
-        ///     Creates a new UdpConnection and initializes the keep alive timer.
-        /// </summary>
-        protected UdpConnection()
-        {
-            InitializeKeepAliveTimer();
-        }
-
         /// <summary>
         ///     Writes the given bytes to the connection.
         /// </summary>
@@ -36,8 +28,8 @@ namespace Hazel.Udp
             switch (msg.SendOption)
             {
                 case SendOption.Reliable:
-                    // Inform keepalive not to send for a while
                     ResetKeepAliveTimer();
+
                     AttachReliableID(buffer, 1, buffer.Length);
                     WriteBytesToConnection(buffer, buffer.Length);
                     Statistics.LogReliableSend(buffer.Length - 3, buffer.Length);
index e1d6f46927e89ec2323446910d0adcb072320645..09ee2ac3a9448cb2bff97368c6a4edb3367775dd 100644 (file)
@@ -1,11 +1,7 @@
 using System;
 using System.Collections.Concurrent;
-using System.Collections.Generic;
-using System.Diagnostics;
-using System.Linq;
 using System.Net;
 using System.Net.Sockets;
-using System.Text;
 using System.Threading;
 
 namespace Hazel.Udp
@@ -20,6 +16,9 @@ namespace Hazel.Udp
 
         public int MinConnectionLength = 0;
 
+        public delegate bool AcceptConnectionCheck(out byte[] response);
+        public AcceptConnectionCheck AcceptConnection;
+
         /// <summary>
         ///     The socket listening for connections.
         /// </summary>
@@ -92,7 +91,7 @@ namespace Hazel.Udp
             }
             catch (SocketException e)
             {
-                throw new HazelException("Could not start listening as a SocketException occured", e);
+                throw new HazelException("Could not start listening as a SocketException occurred", e);
             }
 
             StartListeningForData();
@@ -111,11 +110,14 @@ namespace Hazel.Udp
                 message = MessageReader.GetSized(BufferSize);
 
                 socket.BeginReceiveFrom(message.Buffer, 0, message.Buffer.Length, SocketFlags.None, ref remoteEP, ReadCallback, message);
-                Interlocked.Increment(ref ActiveListeners);
             }
-            catch (SocketException)
+            catch (SocketException sx)
             {
                 message?.Recycle();
+
+                this.Logger?.Invoke("Socket Ex in StartListening: " + sx.Message);
+
+                Thread.Sleep(10);
                 StartListeningForData();
                 return;
             }
@@ -127,20 +129,11 @@ namespace Hazel.Udp
                 return;
             }
         }
-        
-        /// <summary>
-        ///     Called when data has been received by the listener.
-        /// </summary>
-        /// <param name="result">The asyncronous operation's result.</param>
-        
-        public int ActiveListeners;
-        public int PacketsReceived;
 
+        public volatile int ActiveCallbacks;
         void ReadCallback(IAsyncResult result)
         {
-            Interlocked.Decrement(ref ActiveListeners);
-            Interlocked.Increment(ref PacketsReceived);
-
+            Interlocked.Increment(ref this.ActiveCallbacks);
             var message = (MessageReader)result.AsyncState;
             int bytesReceived;
             EndPoint remoteEndPoint = new IPEndPoint(IPMode == IPMode.IPv4 ? IPAddress.Any : IPAddress.IPv6Any, 0);
@@ -153,7 +146,7 @@ namespace Hazel.Udp
                 message.Offset = 0;
                 message.Length = bytesReceived;
             }
-            catch (SocketException)
+            catch (SocketException sx)
             {
                 // Client no longer reachable, pretend it didn't happen
                 // TODO should this not inform the connection this client is lost???
@@ -161,8 +154,11 @@ namespace Hazel.Udp
                 // 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();
-                
+                this.Logger?.Invoke("Socket Ex in ReadCallback: " + sx.Message);
+
+                Thread.Sleep(10);
                 StartListeningForData();
+                Interlocked.Decrement(ref this.ActiveCallbacks);
                 return;
             }
             catch (Exception ex)
@@ -170,6 +166,7 @@ namespace Hazel.Udp
                 //If the socket's been disposed then we can just end there.
                 message.Recycle();
                 this.Logger?.Invoke("Stopped due to: " + ex.Message);
+                Interlocked.Decrement(ref this.ActiveCallbacks);
                 return;
             }
 
@@ -178,7 +175,10 @@ namespace Hazel.Udp
             if (bytesReceived == 0)
             {
                 message.Recycle();
+                this.Logger?.Invoke("Received 0 bytes");
+                Thread.Sleep(10);
                 StartListeningForData();
+                Interlocked.Decrement(ref this.ActiveCallbacks);
                 return;
             }
 
@@ -198,21 +198,26 @@ namespace Hazel.Udp
                 if (!isHello)
                 {
                     message.Recycle();
+                    Interlocked.Decrement(ref this.ActiveCallbacks);
                     return;
                 }
 
-                lock (this.allConnections)
+                if (AcceptConnection != null)
                 {
-                    aware = this.allConnections.TryGetValue(remoteEndPoint, out connection);
-                    if (!aware)
+                    if (!AcceptConnection(out var response))
                     {
-                        connection = new UdpServerConnection(this, (IPEndPoint)remoteEndPoint, this.IPMode);
-                        if (!this.allConnections.TryAdd(remoteEndPoint, connection))
-                        {
-                            throw new Exception();
-                        }
+                        message.Recycle();
+                        SendData(response, response.Length, remoteEndPoint);
+                        Interlocked.Decrement(ref this.ActiveCallbacks);
+                        return;
                     }
                 }
+
+                connection = this.allConnections.GetOrAdd(remoteEndPoint, (ep) =>
+                {
+                    aware = false;
+                    return new UdpServerConnection(this, (IPEndPoint)ep, this.IPMode);
+                });
             }
 
             //Inform the connection of the buffer (new connections need to send an ack back to client)
@@ -231,6 +236,8 @@ namespace Hazel.Udp
             {
                 message.Recycle();
             }
+
+            Interlocked.Decrement(ref this.ActiveCallbacks);
         }
 
 #if DEBUG
@@ -278,7 +285,7 @@ namespace Hazel.Udp
             }
             catch (SocketException e)
             {
-                throw new HazelException("Could not send data as a SocketException occured.", e);
+                throw new HazelException("Could not send data as a SocketException occurred.", e);
             }
             catch (ObjectDisposedException)
             {
@@ -324,13 +331,9 @@ namespace Hazel.Udp
                 kvp.Value.Dispose();
             }
 
-            if (this.socket != null)
-            {
-                try { this.socket.Shutdown(SocketShutdown.Both); } catch { }
-                try { this.socket.Close(); } catch { }
-                try { this.socket.Dispose(); } catch { }
-                this.socket = null;
-            }
+            try { this.socket.Shutdown(SocketShutdown.Both); } catch { }
+            try { this.socket.Close(); } catch { }
+            try { this.socket.Dispose(); } catch { }
 
             this.reliablePacketTimer.Dispose();
 
index 9dad8c73020ef7f4dd8331598e3e7f83056f090e..530920d3086ca90912662a5a6297e25d0cb68147 100644 (file)
@@ -33,6 +33,7 @@ namespace Hazel.Udp
             this.IPMode = IPMode;
 
             State = ConnectionState.Connected;
+            this.InitializeKeepAliveTimer();
         }
 
         /// <inheritdoc />
@@ -97,7 +98,6 @@ namespace Hazel.Udp
                 SendDisconnect();
             }
 
-            
             base.Dispose(disposing);
         }
     }