]> git.deb.at Git - rhonda/impostor.hazel.git/commitdiff
Add more tests, fix a bug when recycling unaware hello messages
authorForest <forest@innersloth.com>
Wed, 19 Aug 2020 21:50:55 +0000 (14:50 -0700)
committerForest <forest@innersloth.com>
Wed, 19 Aug 2020 21:50:55 +0000 (14:50 -0700)
Hazel.UnitTests/Hazel.UnitTests.csproj
Hazel.UnitTests/TestHelper.cs
Hazel.UnitTests/ThreadLimitedUdpConnectionTests.cs [new file with mode: 0644]
Hazel.UnitTests/UdpConnectionTestHarness.cs [new file with mode: 0644]
Hazel.UnitTests/UdpConnectionTests.cs
Hazel.UnitTests/UdpReliabilityTests.cs [new file with mode: 0644]
Hazel/ConnectionListener.cs
Hazel/FewerThreads/ThreadLimitedUdpConnectionListener.cs
Hazel/UPnP/ILogger.cs

index 45305afbf55367ccc7fda4c025e4af63c26c2bf5..e0d01fb5d78b267505119f3f9bd41e6a5b6455b1 100644 (file)
@@ -62,6 +62,7 @@
     <Compile Include="TestHelper.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
     <Compile Include="UdpConnectionTestHarness.cs" />
+    <Compile Include="ThreadLimitedUdpConnectionTests.cs" />
     <Compile Include="UdpConnectionTests.cs" />
     <Compile Include="MessageWriterTests.cs" />
     <Compile Include="StressTests.cs" />
index 0aed2c1edb75eb02633009b0c17f6be1f1e797f3..39071c13cb98640091c258a0ef43646b5fd8e15c 100644 (file)
@@ -5,12 +5,63 @@ using Hazel;
 using System.Net;
 using System.Threading;
 using System.Diagnostics;
+using Hazel.Udp.FewerThreads;
 
 namespace Hazel.UnitTests
 {
     [TestClass]
     public static class TestHelper
     {
+        /// <summary>
+        ///     Runs a general test on the given listener and connection.
+        /// </summary>
+        /// <param name="listener">The listener to test.</param>
+        /// <param name="connection">The connection to test.</param>
+        internal static void RunServerToClientTest(ThreadLimitedUdpConnectionListener listener, Connection connection, int dataSize, SendOption sendOption)
+        {
+            //Setup meta stuff 
+            byte[] data = BuildData(dataSize);
+            ManualResetEvent mutex = new ManualResetEvent(false);
+
+            //Setup listener
+            listener.NewConnection += delegate (NewConnectionEventArgs ncArgs)
+            {
+                ncArgs.Connection.SendBytes(data, sendOption);
+            };
+
+            listener.Start();
+
+            DataReceivedEventArgs? args = null;
+            //Setup conneciton
+            connection.DataReceived += delegate (DataReceivedEventArgs a)
+            {
+                Trace.WriteLine("Data was received correctly.");
+
+                try
+                {
+                    args = a;
+                }
+                finally
+                {
+                    mutex.Set();
+                }
+            };
+
+            connection.Connect();
+
+            //Wait until data is received
+            mutex.WaitOne();
+
+            Assert.AreEqual(data.Length, args.Value.Message.Length);
+
+            for (int i = 0; i < data.Length; i++)
+            {
+                Assert.AreEqual(data[i], args.Value.Message.ReadByte());
+            }
+
+            Assert.AreEqual(sendOption, args.Value.SendOption);
+        }
+
         /// <summary>
         ///     Runs a general test on the given listener and connection.
         /// </summary>
@@ -111,6 +162,57 @@ namespace Hazel.UnitTests
             Assert.AreEqual(sendOption, result.Value.SendOption);
         }
 
+
+        /// <summary>
+        ///     Runs a general test on the given listener and connection.
+        /// </summary>
+        /// <param name="listener">The listener to test.</param>
+        /// <param name="connection">The connection to test.</param>
+        internal static void RunClientToServerTest(ThreadLimitedUdpConnectionListener listener, Connection connection, int dataSize, SendOption sendOption)
+        {
+            //Setup meta stuff 
+            byte[] data = BuildData(dataSize);
+            ManualResetEvent mutex = new ManualResetEvent(false);
+            ManualResetEvent mutex2 = new ManualResetEvent(false);
+
+            //Setup listener
+            DataReceivedEventArgs? result = null;
+            listener.NewConnection += delegate (NewConnectionEventArgs args)
+            {
+                args.Connection.DataReceived += delegate (DataReceivedEventArgs innerArgs)
+                {
+                    Trace.WriteLine("Data was received correctly.");
+
+                    result = innerArgs;
+
+                    mutex2.Set();
+                };
+
+                mutex.Set();
+            };
+
+            listener.Start();
+
+            //Connect
+            connection.Connect();
+
+            mutex.WaitOne();
+
+            connection.SendBytes(data, sendOption);
+
+            //Wait until data is received
+            mutex2.WaitOne();
+
+            Assert.AreEqual(data.Length, result.Value.Message.Length);
+
+            for (int i = 0; i < data.Length; i++)
+            {
+                Assert.AreEqual(data[i], result.Value.Message.ReadByte());
+            }
+
+            Assert.AreEqual(sendOption, result.Value.SendOption);
+        }
+
         /// <summary>
         ///     Runs a server disconnect test on the given listener and connection.
         /// </summary>
diff --git a/Hazel.UnitTests/ThreadLimitedUdpConnectionTests.cs b/Hazel.UnitTests/ThreadLimitedUdpConnectionTests.cs
new file mode 100644 (file)
index 0000000..42b7180
--- /dev/null
@@ -0,0 +1,505 @@
+using System;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System.Net;
+using System.Threading;
+using Hazel.Udp;
+using Hazel.Udp.FewerThreads;
+using System.Net.Sockets;
+
+namespace Hazel.UnitTests
+{
+    [TestClass]
+    public class ThreadLimitedUdpConnectionTests
+    {
+        [TestMethod]
+        public void ServerDisposeDisconnectsTest()
+        {
+            IPEndPoint ep = new IPEndPoint(IPAddress.Loopback, 4296);
+
+            bool serverConnected = false;
+            bool serverDisconnected = false;
+            bool clientDisconnected = false;
+
+            using (ThreadLimitedUdpConnectionListener listener = new ThreadLimitedUdpConnectionListener(2, new IPEndPoint(IPAddress.Any, 4296), new NullLogger()))
+            using (UdpConnection connection = new UdpClientConnection(ep))
+            {
+                listener.NewConnection += (evt) =>
+                {
+                    serverConnected = true;
+                    evt.Connection.Disconnected += (o, et) => serverDisconnected = true;
+                };
+                connection.Disconnected += (o, evt) => clientDisconnected = true;
+
+                listener.Start();
+                connection.Connect();
+
+                Thread.Sleep(100); // Gotta wait for the server to set up the events.
+                listener.Dispose();
+                Thread.Sleep(100);
+
+                Assert.IsTrue(serverConnected);
+                Assert.IsTrue(clientDisconnected);
+                Assert.IsFalse(serverDisconnected);
+            }
+        }
+
+        [TestMethod]
+        public void ClientServerDisposeDisconnectsTest()
+        {
+            IPEndPoint ep = new IPEndPoint(IPAddress.Loopback, 4296);
+
+            bool serverConnected = false;
+            bool serverDisconnected = false;
+            bool clientDisconnected = false;
+
+            using (ThreadLimitedUdpConnectionListener listener = new ThreadLimitedUdpConnectionListener(2, new IPEndPoint(IPAddress.Any, 4296), new NullLogger()))
+            using (UdpConnection connection = new UdpClientConnection(ep))
+            {
+                listener.NewConnection += (evt) =>
+                {
+                    serverConnected = true;
+                    evt.Connection.Disconnected += (o, et) => serverDisconnected = true;
+                };
+
+                connection.Disconnected += (o, et) => clientDisconnected = true;
+
+                listener.Start();
+                connection.Connect();
+
+                Thread.Sleep(100); // Gotta wait for the server to set up the events.
+                connection.Dispose();
+
+                Thread.Sleep(100);
+
+                Assert.IsTrue(serverConnected);
+                Assert.IsTrue(serverDisconnected);
+                Assert.IsFalse(clientDisconnected);
+            }
+        }
+
+        /// <summary>
+        ///     Tests the fields on UdpConnection.
+        /// </summary>
+        [TestMethod]
+        public void UdpFieldTest()
+        {
+            IPEndPoint ep = new IPEndPoint(IPAddress.Loopback, 4296);
+
+            using (ThreadLimitedUdpConnectionListener listener = new ThreadLimitedUdpConnectionListener(2, new IPEndPoint(IPAddress.Any, 4296), new NullLogger()))
+            using (UdpConnection connection = new UdpClientConnection(ep))
+            {
+                listener.Start();
+
+                connection.Connect();
+
+                //Connection fields
+                Assert.AreEqual(ep, connection.EndPoint);
+
+                //UdpConnection fields
+                Assert.AreEqual(new IPEndPoint(IPAddress.Loopback, 4296), connection.RemoteEndPoint);
+                Assert.AreEqual(1, connection.Statistics.DataBytesSent);
+                Assert.AreEqual(0, connection.Statistics.DataBytesReceived);
+            }
+        }
+
+        [TestMethod]
+        public void UdpHandshakeTest()
+        {
+            byte[] TestData = new byte[] { 1, 2, 3, 4, 5, 6 };
+            using (ThreadLimitedUdpConnectionListener listener = new ThreadLimitedUdpConnectionListener(2, new IPEndPoint(IPAddress.Any, 4296), new NullLogger()))
+            using (UdpConnection connection = new UdpClientConnection(new IPEndPoint(IPAddress.Loopback, 4296)))
+            {
+                listener.Start();
+
+                MessageReader output = null;
+                listener.NewConnection += delegate (NewConnectionEventArgs e)
+                {
+                    output = e.HandshakeData;
+                };
+
+                connection.Connect(TestData);
+
+                Thread.Sleep(10);
+                for (int i = 0; i < TestData.Length; ++i)
+                {
+                    Assert.AreEqual(TestData[i], output.ReadByte());
+                }
+            }
+        }
+
+        [TestMethod]
+        public void UdpUnreliableMessageSendTest()
+        {
+            byte[] TestData = new byte[] { 1, 2, 3, 4, 5, 6 };
+            using (ThreadLimitedUdpConnectionListener listener = new ThreadLimitedUdpConnectionListener(2, new IPEndPoint(IPAddress.Any, 4296), new NullLogger()))
+            using (UdpConnection connection = new UdpClientConnection(new IPEndPoint(IPAddress.Loopback, 4296)))
+            {
+                MessageReader output = null;
+                listener.NewConnection += delegate (NewConnectionEventArgs e)
+                {
+                    e.Connection.DataReceived += delegate (DataReceivedEventArgs evt)
+                    {
+                        output = evt.Message;
+                    };
+                };
+
+                listener.Start();
+                connection.Connect();
+
+                for (int i = 0; i < 4; ++i)
+                {
+                    var msg = MessageWriter.Get(SendOption.None);
+                    msg.Write(TestData);
+                    connection.Send(msg);
+                    msg.Recycle();
+                }
+
+                Thread.Sleep(10);
+                for (int i = 0; i < TestData.Length; ++i)
+                {
+                    Assert.AreEqual(TestData[i], output.ReadByte());
+                }
+            }
+        }
+
+        /// <summary>
+        ///     Tests IPv4 connectivity.
+        /// </summary>
+        [TestMethod]
+        public void UdpIPv4ConnectionTest()
+        {
+            using (ThreadLimitedUdpConnectionListener listener = new ThreadLimitedUdpConnectionListener(2, new IPEndPoint(IPAddress.Any, 4296), new NullLogger()))
+            using (UdpConnection connection = new UdpClientConnection(new IPEndPoint(IPAddress.Loopback, 4296)))
+            {
+                listener.Start();
+
+                connection.Connect();
+            }
+        }
+
+        /// <summary>
+        ///     Tests IPv4 resilience to multiple hellos.
+        /// </summary>
+        [TestMethod]
+        public void ConnectLikeAJerkTest()
+        {
+            using (ThreadLimitedUdpConnectionListener listener = new ThreadLimitedUdpConnectionListener(2, new IPEndPoint(IPAddress.Any, 4296), new NullLogger()))
+            using (Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp))
+            {
+                int connects = 0;
+                listener.NewConnection += (obj) =>
+                {
+                    Interlocked.Increment(ref connects);
+                    obj.HandshakeData.Recycle();
+                };
+
+                listener.Start();
+
+                socket.Bind(new IPEndPoint(IPAddress.Any, 0));
+                var bytes = new byte[2];
+                bytes[0] = (byte)UdpSendOption.Hello;
+                for (int i = 0; i < 10; ++i)
+                {
+                    socket.SendTo(bytes, new IPEndPoint(IPAddress.Loopback, 4296));
+                }
+
+                Thread.Sleep(500);
+
+                Assert.AreEqual(0, listener.ReceiveQueueLength);
+                Assert.AreEqual(1, connects);
+            }
+        }
+
+        /// <summary>
+        ///     Tests dual mode connectivity.
+        /// </summary>
+        [TestMethod]
+        public void MixedConnectionTest()
+        {
+            
+            using (ThreadLimitedUdpConnectionListener listener2 = new ThreadLimitedUdpConnectionListener(4, new IPEndPoint(IPAddress.IPv6Any, 4296), new ConsoleLogger(), IPMode.IPv6))
+            {
+                listener2.Start();
+
+                listener2.NewConnection += (evt) =>
+                {
+                    Console.WriteLine($"Connection: {evt.Connection.EndPoint}");
+                };
+
+                using (UdpConnection connection = new UdpClientConnection(new IPEndPoint(IPAddress.Parse("127.0.0.1"), 4296)))
+                {
+                    connection.Connect();
+                    Assert.AreEqual(ConnectionState.Connected, connection.State);
+                }
+
+                using (UdpConnection connection2 = new UdpClientConnection(new IPEndPoint(IPAddress.IPv6Loopback, 4296), IPMode.IPv6))
+                {
+                    connection2.Connect();
+                    Assert.AreEqual(ConnectionState.Connected, connection2.State);
+                }
+            }
+        }
+
+        /// <summary>
+        ///     Tests dual mode connectivity.
+        /// </summary>
+        [TestMethod]
+        public void UdpIPv6ConnectionTest()
+        {
+            using (ThreadLimitedUdpConnectionListener listener = new ThreadLimitedUdpConnectionListener(2, new IPEndPoint(IPAddress.Any, 4296), new NullLogger(), IPMode.IPv6))
+            {
+                listener.Start();
+
+                using (UdpConnection connection = new UdpClientConnection(new IPEndPoint(IPAddress.Parse("127.0.0.1"), 4296), IPMode.IPv6))
+                {
+                    connection.Connect();
+                }
+            }
+        }
+
+        /// <summary>
+        ///     Tests server to client unreliable communication on the UdpConnection.
+        /// </summary>
+        [TestMethod]
+        public void UdpUnreliableServerToClientTest()
+        {
+            using (ThreadLimitedUdpConnectionListener listener = new ThreadLimitedUdpConnectionListener(2, new IPEndPoint(IPAddress.Any, 4296), new NullLogger()))
+            using (UdpConnection connection = new UdpClientConnection(new IPEndPoint(IPAddress.Loopback, 4296)))
+            {
+                TestHelper.RunServerToClientTest(listener, connection, 10, SendOption.None);
+            }
+        }
+
+        /// <summary>
+        ///     Tests server to client reliable communication on the UdpConnection.
+        /// </summary>
+        [TestMethod]
+        public void UdpReliableServerToClientTest()
+        {
+            using (ThreadLimitedUdpConnectionListener listener = new ThreadLimitedUdpConnectionListener(2, new IPEndPoint(IPAddress.Any, 4296), new NullLogger()))
+            using (UdpConnection connection = new UdpClientConnection(new IPEndPoint(IPAddress.Loopback, 4296)))
+            {
+                TestHelper.RunServerToClientTest(listener, connection, 10, SendOption.Reliable);
+            }
+        }
+        
+        /// <summary>
+        ///     Tests server to client unreliable communication on the UdpConnection.
+        /// </summary>
+        [TestMethod]
+        public void UdpUnreliableClientToServerTest()
+        {
+            using (ThreadLimitedUdpConnectionListener listener = new ThreadLimitedUdpConnectionListener(2, new IPEndPoint(IPAddress.Any, 4296), new NullLogger()))
+            using (UdpConnection connection = new UdpClientConnection(new IPEndPoint(IPAddress.Loopback, 4296)))
+            {
+                TestHelper.RunClientToServerTest(listener, connection, 10, SendOption.None);
+            }
+        }
+
+        /// <summary>
+        ///     Tests server to client reliable communication on the UdpConnection.
+        /// </summary>
+        [TestMethod]
+        public void UdpReliableClientToServerTest()
+        {
+            using (ThreadLimitedUdpConnectionListener listener = new ThreadLimitedUdpConnectionListener(2, new IPEndPoint(IPAddress.Any, 4296), new NullLogger()))
+            using (UdpConnection connection = new UdpClientConnection(new IPEndPoint(IPAddress.Loopback, 4296)))
+            {
+                TestHelper.RunClientToServerTest(listener, connection, 10, SendOption.Reliable);
+            }
+        }
+
+        /// <summary>
+        ///     Tests the keepalive functionality from the client,
+        /// </summary>
+        [TestMethod]
+        public void PingDisconnectClientTest()
+        {
+#if DEBUG
+            using (ThreadLimitedUdpConnectionListener listener = new ThreadLimitedUdpConnectionListener(2, new IPEndPoint(IPAddress.Any, 4296), new NullLogger()))
+            using (UdpConnection connection = new UdpClientConnection(new IPEndPoint(IPAddress.Loopback, 4296)))
+            {
+                listener.Start();
+
+                connection.Connect();
+
+                // After connecting, quietly stop responding to all messages to fake connection loss.
+                Thread.Sleep(10);
+                // listener.TestDropRate = 1;
+
+                connection.KeepAliveInterval = 100;
+
+                Thread.Sleep(1050);    //Enough time for ~10 keep alive packets
+
+                Assert.AreEqual(ConnectionState.NotConnected, connection.State);
+                Assert.AreEqual(3 * connection.MissingPingsUntilDisconnect + 4, connection.Statistics.TotalBytesSent); // + 4 for connecting overhead
+            }
+#else
+            Assert.Inconclusive("Only works in DEBUG");
+#endif
+        }
+
+        /// <summary>
+        ///     Tests the keepalive functionality from the client,
+        /// </summary>
+        [TestMethod]
+        public void KeepAliveClientTest()
+        {
+            using (ThreadLimitedUdpConnectionListener listener = new ThreadLimitedUdpConnectionListener(2, new IPEndPoint(IPAddress.Any, 4296), new NullLogger()))
+            using (UdpConnection connection = new UdpClientConnection(new IPEndPoint(IPAddress.Loopback, 4296)))
+            {
+                listener.Start();
+
+                connection.Connect();
+                connection.KeepAliveInterval = 100;
+
+                Thread.Sleep(1050);    //Enough time for ~10 keep alive packets
+
+                Assert.AreEqual(ConnectionState.Connected, connection.State);
+                Assert.IsTrue(
+                    connection.Statistics.TotalBytesSent >= 30 &&
+                    connection.Statistics.TotalBytesSent <= 50,
+                    "Sent: " + connection.Statistics.TotalBytesSent
+                );
+            }
+        }
+
+        /// <summary>
+        ///     Tests the keepalive functionality from the client,
+        /// </summary>
+        [TestMethod]
+        public void KeepAliveServerTest()
+        {
+            ManualResetEvent mutex = new ManualResetEvent(false);
+
+            using (ThreadLimitedUdpConnectionListener listener = new ThreadLimitedUdpConnectionListener(2, new IPEndPoint(IPAddress.Any, 4296), new NullLogger()))
+            using (UdpConnection connection = new UdpClientConnection(new IPEndPoint(IPAddress.Loopback, 4296)))
+            {
+                UdpConnection client = null;
+                listener.NewConnection += delegate (NewConnectionEventArgs args)
+                {
+                    client = (UdpConnection)args.Connection;
+                    client.KeepAliveInterval = 100;
+
+                    Thread.Sleep(1050);    //Enough time for ~10 keep alive packets
+
+                    mutex.Set();
+                };
+
+                listener.Start();
+
+                connection.Connect();
+
+                mutex.WaitOne();
+
+                Assert.AreEqual(ConnectionState.Connected, client.State);
+
+                Assert.IsTrue(
+                    client.Statistics.TotalBytesSent >= 27 &&
+                    client.Statistics.TotalBytesSent <= 50,
+                    "Sent: " + client.Statistics.TotalBytesSent
+                );
+            }
+        }
+
+        /// <summary>
+        ///     Tests disconnection from the client.
+        /// </summary>
+        [TestMethod]
+        public void ClientDisconnectTest()
+        {
+            using (ThreadLimitedUdpConnectionListener listener = new ThreadLimitedUdpConnectionListener(2, new IPEndPoint(IPAddress.Any, 4296), new NullLogger()))
+            using (UdpConnection connection = new UdpClientConnection(new IPEndPoint(IPAddress.Loopback, 4296)))
+            {
+                ManualResetEvent mutex = new ManualResetEvent(false);
+                ManualResetEvent mutex2 = new ManualResetEvent(false);
+
+                listener.NewConnection += delegate (NewConnectionEventArgs args)
+                {
+                    args.Connection.Disconnected += delegate (object sender2, DisconnectedEventArgs args2)
+                    {
+                        mutex2.Set();
+                    };
+
+                    mutex.Set();
+                };
+
+                listener.Start();
+
+                connection.Connect();
+
+                mutex.WaitOne();
+
+                connection.Disconnect("Testing");
+
+                mutex2.WaitOne();
+            }
+        }
+
+        /// <summary>
+        ///     Tests disconnection from the server.
+        /// </summary>
+        [TestMethod]
+        public void ServerDisconnectTest()
+        {
+            using (ThreadLimitedUdpConnectionListener listener = new ThreadLimitedUdpConnectionListener(2, new IPEndPoint(IPAddress.Any, 4296), new NullLogger()))
+            using (UdpConnection connection = new UdpClientConnection(new IPEndPoint(IPAddress.Loopback, 4296)))
+            {
+
+                ManualResetEvent mutex = new ManualResetEvent(false);
+
+                connection.Disconnected += delegate (object sender, DisconnectedEventArgs args)
+                {
+                    mutex.Set();
+                };
+
+                listener.NewConnection += delegate (NewConnectionEventArgs args)
+                {
+                    args.Connection.Disconnect("Testing");
+                };
+
+                listener.Start();
+
+                connection.Connect();
+
+                mutex.WaitOne();
+            }
+        }
+
+        /// <summary>
+        ///     Tests disconnection from the server.
+        /// </summary>
+        [TestMethod]
+        public void ServerExtraDataDisconnectTest()
+        {
+            using (ThreadLimitedUdpConnectionListener listener = new ThreadLimitedUdpConnectionListener(2, new IPEndPoint(IPAddress.Any, 4296), new NullLogger()))
+            using (UdpConnection connection = new UdpClientConnection(new IPEndPoint(IPAddress.Loopback, 4296)))
+            {
+                MessageReader received = null;
+                ManualResetEvent mutex = new ManualResetEvent(false);
+
+                connection.Disconnected += delegate (object sender, DisconnectedEventArgs args)
+                {
+                    received = args.Message;
+                    mutex.Set();
+                };
+
+                listener.NewConnection += delegate (NewConnectionEventArgs args)
+                {
+                    MessageWriter writer = MessageWriter.Get(SendOption.None);
+                    writer.Write("Goodbye");
+                    args.Connection.Disconnect("Testing", writer);
+                };
+
+                listener.Start();
+
+                connection.Connect();
+
+                mutex.WaitOne();
+
+                Assert.IsNotNull(received);
+                Assert.AreEqual("Goodbye", received.ReadString());
+            }
+        }
+    }
+}
diff --git a/Hazel.UnitTests/UdpConnectionTestHarness.cs b/Hazel.UnitTests/UdpConnectionTestHarness.cs
new file mode 100644 (file)
index 0000000..c7cde8d
--- /dev/null
@@ -0,0 +1,53 @@
+using Hazel.Udp;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Hazel.UnitTests
+{
+    internal class UdpConnectionTestHarness : UdpConnection
+    {
+        public List<MessageReader> BytesSent = new List<MessageReader>();
+
+        public override void Connect(byte[] bytes = null, int timeout = 5000)
+        {
+            this.State = ConnectionState.Connected;
+        }
+
+        public override void ConnectAsync(byte[] bytes = null)
+        {
+            this.State = ConnectionState.Connected;
+        }
+
+        protected override bool SendDisconnect(MessageWriter writer)
+        {
+            lock (this)
+            {
+                if (this.State != ConnectionState.Connected)
+                {
+                    return false;
+                }
+
+                this.State = ConnectionState.NotConnected;
+            }
+
+            return true;
+        }
+
+        protected override void WriteBytesToConnection(byte[] bytes, int length)
+        {
+            this.BytesSent.Add(MessageReader.Get(bytes));
+        }
+
+        public void Test_Receive(MessageWriter msg)
+        {
+            byte[] buffer = new byte[msg.Length];
+            Buffer.BlockCopy(msg.Buffer, 0, buffer, 0, msg.Length);
+
+            var data = MessageReader.Get(buffer);
+            this.HandleReceive(data, data.Length);
+        }
+    }
+}
index f7a95c03d56ba3caea127292c30a8943df40f408..73017d745ee866261361cda9b73d56a6f9ad1690 100644 (file)
@@ -3,6 +3,7 @@ using Microsoft.VisualStudio.TestTools.UnitTesting;
 using System.Net;
 using System.Threading;
 using Hazel.Udp;
+using System.Net.Sockets;
 
 namespace Hazel.UnitTests
 {
@@ -204,6 +205,70 @@ namespace Hazel.UnitTests
             }
         }
 
+        /// <summary>
+        ///     Tests IPv4 resilience to non-hello connections.
+        /// </summary>
+        [TestMethod]
+        public void FalseConnectionTest()
+        {
+            using (UdpConnectionListener listener = new UdpConnectionListener(new IPEndPoint(IPAddress.Any, 4296)))
+            using (Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp))
+            {
+                int connects = 0;
+                listener.NewConnection += (obj) =>
+                {
+                    Interlocked.Increment(ref connects);
+                    obj.HandshakeData.Recycle();
+                };
+
+                listener.Start();
+
+                socket.Bind(new IPEndPoint(IPAddress.Any, 0));
+                var bytes = new byte[2];
+                bytes[0] = (byte)32;
+                for (int i = 0; i < 10; ++i)
+                {
+                    socket.SendTo(bytes, new IPEndPoint(IPAddress.Loopback, 4296));
+                }
+
+                Thread.Sleep(500);
+
+                Assert.AreEqual(0, connects);
+            }
+        }
+
+        /// <summary>
+        ///     Tests IPv4 resilience to multiple hellos.
+        /// </summary>
+        [TestMethod]
+        public void ConnectLikeAJerkTest()
+        {
+            using (UdpConnectionListener listener = new UdpConnectionListener(new IPEndPoint(IPAddress.Any, 4296)))
+            using (Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp))
+            {
+                int connects = 0;
+                listener.NewConnection += (obj) =>
+                {
+                    Interlocked.Increment(ref connects);
+                    obj.HandshakeData.Recycle();
+                };
+
+                listener.Start();
+
+                socket.Bind(new IPEndPoint(IPAddress.Any, 0));
+                var bytes = new byte[2];
+                bytes[0] = (byte)UdpSendOption.Hello;
+                for (int i = 0; i < 10; ++i)
+                {
+                    socket.SendTo(bytes, new IPEndPoint(IPAddress.Loopback, 4296));
+                }
+
+                Thread.Sleep(500);
+
+                Assert.AreEqual(1, connects);
+            }
+        }
+
         /// <summary>
         ///     Tests dual mode connectivity.
         /// </summary>
diff --git a/Hazel.UnitTests/UdpReliabilityTests.cs b/Hazel.UnitTests/UdpReliabilityTests.cs
new file mode 100644 (file)
index 0000000..d313cdf
--- /dev/null
@@ -0,0 +1,56 @@
+using System;
+using System.Collections.Generic;
+using Hazel.Udp;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+namespace Hazel.UnitTests
+{
+    [TestClass]
+    public class UdpReliabilityTests
+    {
+        [TestMethod]
+        public void TestThatAllMessagesAreReceived()
+        {
+            List<MessageReader> messagesReceived = new List<MessageReader>();
+
+            UdpConnectionTestHarness dut = new UdpConnectionTestHarness();
+            dut.DataReceived += evt =>
+            {
+                messagesReceived.Add(evt.Message);
+            };
+
+            MessageWriter data = MessageWriter.Get(SendOption.Reliable);
+
+            for (int i = 1; i < ushort.MaxValue * 2; ++i)
+            {
+                // Send a new message, it should be received and ack'd
+                SetReliableId(data, i);
+                dut.Test_Receive(data);
+
+                // Resend an old message, it should be ignored
+                if (i > 2)
+                {
+                    SetReliableId(data, i - 1);
+                    dut.Test_Receive(data);
+
+                    // It should still be ack'd
+                    Assert.AreEqual(2, dut.BytesSent.Count);
+                    dut.BytesSent.RemoveAt(1);
+                }
+
+                Assert.AreEqual(1, messagesReceived.Count);
+                messagesReceived.Clear();
+
+                Assert.AreEqual(1, dut.BytesSent.Count);
+                dut.BytesSent.Clear();
+            }
+        }
+
+        private static void SetReliableId(MessageWriter data, int i)
+        {
+            ushort id = (ushort)i;
+            data.Buffer[1] = (byte)(id >> 8);
+            data.Buffer[2] = (byte)id;
+        }
+    }
+}
index 3facfe2050af1ac1ad588586a4173dbfe9df9e3b..aa830c10f44cbf3a6b4119def18028312aca7138 100644 (file)
@@ -71,7 +71,11 @@ namespace Hazel
             Action<NewConnectionEventArgs> handler = NewConnection;
             if (handler != null)
             {
-                handler(new NewConnectionEventArgs(msg, connection));
+                try
+                {
+                    handler(new NewConnectionEventArgs(msg, connection));
+                }
+                catch { }
             }
             else
             {
index df5e00092d0322f9bbe1ce03819c6b7e5370d634..87e8fbfd9ab597d1ab04719a41138d36ee94103b 100644 (file)
@@ -268,7 +268,7 @@ namespace Hazel.Udp.FewerThreads
             // Inform the connection of the buffer (new connections need to send an ack back to client)
             connection.HandleReceive(message, bytesReceived);
 
-            if (isHello)
+            if (isHello && aware)
             {
                 message.Recycle();
             }
index 0f89e9c6da37ff51cf26e959e6305d83faa106e5..0bfb2df3e89adec62bb738f0e95a852686611045 100644 (file)
@@ -24,4 +24,17 @@ namespace Hazel
         {
         }
     }
+
+    public class ConsoleLogger : ILogger
+    {
+        public void WriteError(string msg)
+        {
+            Console.WriteLine($"{DateTime.Now} [ERROR] {msg}");
+        }
+
+        public void WriteInfo(string msg)
+        {
+            Console.WriteLine($"{DateTime.Now} [INFO] {msg}");
+        }
+    }
 }