]> git.deb.at Git - rhonda/impostor.hazel.git/commitdiff
Initial Commit
authorJamJar00 <jamster.30@btinternet.com>
Sun, 3 Apr 2016 16:08:59 +0000 (17:08 +0100)
committerJamJar00 <jamster.30@btinternet.com>
Sun, 3 Apr 2016 16:08:59 +0000 (17:08 +0100)
28 files changed:
Hazel.UnitTests/Hazel.UnitTests.csproj [new file with mode: 0644]
Hazel.UnitTests/Properties/AssemblyInfo.cs [new file with mode: 0644]
Hazel.UnitTests/TcpConnectionTests.cs [new file with mode: 0644]
Hazel.UnitTests/TestHelper.cs [new file with mode: 0644]
Hazel.UnitTests/UdpConnectionTests.cs [new file with mode: 0644]
Hazel.sln [new file with mode: 0644]
Hazel/Connection.cs [new file with mode: 0644]
Hazel/ConnectionEndPoint.cs [new file with mode: 0644]
Hazel/ConnectionListener.cs [new file with mode: 0644]
Hazel/ConnectionState.cs [new file with mode: 0644]
Hazel/ConnectionStatistics.cs [new file with mode: 0644]
Hazel/DataEventArgs.cs [new file with mode: 0644]
Hazel/DisconnectedEventArgs.cs [new file with mode: 0644]
Hazel/Hazel.csproj [new file with mode: 0644]
Hazel/Hazel.snk [new file with mode: 0644]
Hazel/HazelException.cs [new file with mode: 0644]
Hazel/NetworkEndPoint.cs [new file with mode: 0644]
Hazel/NewConnectionEventArgs.cs [new file with mode: 0644]
Hazel/Properties/AssemblyInfo.cs [new file with mode: 0644]
Hazel/SendFlags.cs [new file with mode: 0644]
Hazel/StateObject.cs [new file with mode: 0644]
Hazel/TcpConnection.cs [new file with mode: 0644]
Hazel/TcpConnectionListener.cs [new file with mode: 0644]
Hazel/UdpClientConnection.cs [new file with mode: 0644]
Hazel/UdpConnection.cs [new file with mode: 0644]
Hazel/UdpConnectionListener.cs [new file with mode: 0644]
Hazel/UdpServerConnection.cs [new file with mode: 0644]
Hazel/Utility.cs [new file with mode: 0644]

diff --git a/Hazel.UnitTests/Hazel.UnitTests.csproj b/Hazel.UnitTests/Hazel.UnitTests.csproj
new file mode 100644 (file)
index 0000000..62520fd
--- /dev/null
@@ -0,0 +1,91 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProjectGuid>{1394E4CA-E17A-42F5-9216-8046ACA8D16B}</ProjectGuid>
+    <OutputType>Library</OutputType>
+    <AppDesignerFolder>Properties</AppDesignerFolder>
+    <RootNamespace>Hazel.UnitTests</RootNamespace>
+    <AssemblyName>Hazel.UnitTests</AssemblyName>
+    <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
+    <FileAlignment>512</FileAlignment>
+    <ProjectTypeGuids>{3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+    <VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
+    <VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
+    <ReferencePath>$(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages</ReferencePath>
+    <IsCodedUITest>False</IsCodedUITest>
+    <TestProjectType>UnitTest</TestProjectType>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>bin\Debug\</OutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <DebugType>pdbonly</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>bin\Release\</OutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="System" />
+  </ItemGroup>
+  <Choose>
+    <When Condition="('$(VisualStudioVersion)' == '10.0' or '$(VisualStudioVersion)' == '') and '$(TargetFrameworkVersion)' == 'v3.5'">
+      <ItemGroup>
+        <Reference Include="Microsoft.VisualStudio.QualityTools.UnitTestFramework, Version=10.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
+      </ItemGroup>
+    </When>
+    <Otherwise>
+      <ItemGroup>
+        <Reference Include="Microsoft.VisualStudio.QualityTools.UnitTestFramework" />
+      </ItemGroup>
+    </Otherwise>
+  </Choose>
+  <ItemGroup>
+    <Compile Include="TestHelper.cs" />
+    <Compile Include="Properties\AssemblyInfo.cs" />
+    <Compile Include="TcpConnectionTests.cs" />
+    <Compile Include="UdpConnectionTests.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\Hazel\Hazel.csproj">
+      <Project>{02cfbd30-d77d-400f-94b2-700f60efdd7f}</Project>
+      <Name>Hazel</Name>
+    </ProjectReference>
+  </ItemGroup>
+  <Choose>
+    <When Condition="'$(VisualStudioVersion)' == '10.0' And '$(IsCodedUITest)' == 'True'">
+      <ItemGroup>
+        <Reference Include="Microsoft.VisualStudio.QualityTools.CodedUITestFramework, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
+          <Private>False</Private>
+        </Reference>
+        <Reference Include="Microsoft.VisualStudio.TestTools.UITest.Common, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
+          <Private>False</Private>
+        </Reference>
+        <Reference Include="Microsoft.VisualStudio.TestTools.UITest.Extension, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
+          <Private>False</Private>
+        </Reference>
+        <Reference Include="Microsoft.VisualStudio.TestTools.UITesting, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
+          <Private>False</Private>
+        </Reference>
+      </ItemGroup>
+    </When>
+  </Choose>
+  <Import Project="$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets" Condition="Exists('$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets')" />
+  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
+       Other similar extension points exist, see Microsoft.Common.targets.
+  <Target Name="BeforeBuild">
+  </Target>
+  <Target Name="AfterBuild">
+  </Target>
+  -->
+</Project>
\ No newline at end of file
diff --git a/Hazel.UnitTests/Properties/AssemblyInfo.cs b/Hazel.UnitTests/Properties/AssemblyInfo.cs
new file mode 100644 (file)
index 0000000..c6e5128
--- /dev/null
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following 
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("Hazel.UnitTests")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("Hazel.UnitTests")]
+[assembly: AssemblyCopyright("Copyright ©  2016")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible 
+// to COM components.  If you need to access a type in this assembly from 
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("e88c2226-946e-4f00-9336-8d8d7946ac23")]
+
+// Version information for an assembly consists of the following four values:
+//
+//      Major Version
+//      Minor Version 
+//      Build Number
+//      Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers 
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/Hazel.UnitTests/TcpConnectionTests.cs b/Hazel.UnitTests/TcpConnectionTests.cs
new file mode 100644 (file)
index 0000000..cd0bdf5
--- /dev/null
@@ -0,0 +1,47 @@
+using System;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System.Net;
+
+namespace Hazel.UnitTests
+{
+    [TestClass]
+    public class TcpConnectionTests
+    {
+        /// <summary>
+        ///     Tests the fields on TcpConnection.
+        /// </summary>
+        [TestMethod]
+        public void TcpConnectionFieldTest()
+        {
+            using (TcpConnectionListener listener = new TcpConnectionListener(IPAddress.Any, 4296))
+            using (TcpConnection connection = new TcpConnection())
+            {
+                listener.Start();
+
+                NetworkEndPoint ep = new NetworkEndPoint(IPAddress.Loopback, 4296);
+                connection.Connect(ep);
+
+                //Connection fields
+                Assert.AreEqual(ep, connection.EndPoint);
+
+                //TcpConnection fields
+                Assert.AreEqual(new IPEndPoint(IPAddress.Loopback, 4296), connection.RemoteEndPoint);
+                Assert.AreEqual(0, connection.Statistics.DataBytesSent);
+                Assert.AreEqual(0, connection.Statistics.DataBytesReceived);
+            }
+        }
+
+        /// <summary>
+        ///     Tests sending and receiving on the TcpConnection.
+        /// </summary>
+        [TestMethod]
+        public void TcpConnectionSendReceiveTest()
+        {
+            using (TcpConnectionListener listener = new TcpConnectionListener(IPAddress.Any, 4296))
+            using (TcpConnection connection = new TcpConnection())
+            {
+                TestHelper.RunSendReceiveTest(listener, connection, 4, 0, 0);
+            }
+        }
+    }
+}
diff --git a/Hazel.UnitTests/TestHelper.cs b/Hazel.UnitTests/TestHelper.cs
new file mode 100644 (file)
index 0000000..3e53b0c
--- /dev/null
@@ -0,0 +1,61 @@
+using System;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+using Hazel;
+using System.Net;
+using System.Threading;
+using System.Diagnostics;
+
+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 RunSendReceiveTest(ConnectionListener listener, Connection connection, int headerSize, int handshakeSize, int totalHandshakeSize)
+        {
+            //Setup meta stuff 
+            byte[] data = new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
+            AutoResetEvent mutex = new AutoResetEvent(false);
+
+            //Setup listener
+            listener.NewConnection += delegate(object sender, NewConnectionEventArgs args)
+            {
+                args.Connection.WriteBytes(data);
+                Assert.AreEqual(data.Length, args.Connection.Statistics.DataBytesSent);
+                Assert.AreEqual(0, args.Connection.Statistics.DataBytesReceived);
+                Assert.AreEqual(data.Length + headerSize, args.Connection.Statistics.TotalBytesSent);
+                Assert.AreEqual(0, args.Connection.Statistics.TotalBytesReceived);
+            };
+
+            listener.Start();
+
+            //Setup conneciton
+            connection.DataReceived += delegate(object sender, DataEventArgs args)
+            {
+                Trace.WriteLine("Data was received correctly.");
+
+                for (int i = 0; i < data.Length; i++)
+                {
+                    Assert.AreEqual(data[i], args.Bytes[i]);
+                }
+
+                mutex.Set();
+            };
+
+            connection.Connect(new NetworkEndPoint(IPAddress.Loopback, 4296));
+
+            //Wait until data is received
+            mutex.WaitOne();
+
+            Assert.AreEqual(handshakeSize, connection.Statistics.DataBytesSent);
+            Assert.AreEqual(data.Length, connection.Statistics.DataBytesReceived);
+            Assert.AreEqual(totalHandshakeSize, connection.Statistics.TotalBytesSent);
+            Assert.AreEqual(data.Length + headerSize, connection.Statistics.TotalBytesReceived);
+        }
+    }
+}
diff --git a/Hazel.UnitTests/UdpConnectionTests.cs b/Hazel.UnitTests/UdpConnectionTests.cs
new file mode 100644 (file)
index 0000000..13b733f
--- /dev/null
@@ -0,0 +1,47 @@
+using System;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using System.Net;
+
+namespace Hazel.UnitTests
+{
+    [TestClass]
+    public class UdpConnectionTests
+    {
+        /// <summary>
+        ///     Tests the fields on UdpConnection.
+        /// </summary>
+        [TestMethod]
+        public void UdpConnectionFieldTest()
+        {
+            using (UdpConnectionListener listener = new UdpConnectionListener(IPAddress.Any, 4296))
+            using (UdpConnection connection = new UdpClientConnection())
+            {
+                listener.Start();
+
+                NetworkEndPoint ep = new NetworkEndPoint(IPAddress.Loopback, 4296);
+                connection.Connect(ep);
+
+                //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);
+            }
+        }
+
+        /// <summary>
+        ///     Tests sending and receiving on the UdpConnection.
+        /// </summary>
+        [TestMethod]
+        public void UdpConnectionSendReceiveTest()
+        {
+            using (UdpConnectionListener listener = new UdpConnectionListener(IPAddress.Any, 4296))
+            using (UdpConnection connection = new UdpClientConnection())
+            {
+                TestHelper.RunSendReceiveTest(listener, connection, 1, 1, 2);
+            }
+        }
+    }
+}
diff --git a/Hazel.sln b/Hazel.sln
new file mode 100644 (file)
index 0000000..02013f7
--- /dev/null
+++ b/Hazel.sln
@@ -0,0 +1,28 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 2013
+VisualStudioVersion = 12.0.31101.0
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Hazel", "Hazel\Hazel.csproj", "{02CFBD30-D77D-400F-94B2-700F60EFDD7F}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Hazel.UnitTests", "Hazel.UnitTests\Hazel.UnitTests.csproj", "{1394E4CA-E17A-42F5-9216-8046ACA8D16B}"
+EndProject
+Global
+       GlobalSection(SolutionConfigurationPlatforms) = preSolution
+               Debug|Any CPU = Debug|Any CPU
+               Release|Any CPU = Release|Any CPU
+       EndGlobalSection
+       GlobalSection(ProjectConfigurationPlatforms) = postSolution
+               {02CFBD30-D77D-400F-94B2-700F60EFDD7F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+               {02CFBD30-D77D-400F-94B2-700F60EFDD7F}.Debug|Any CPU.Build.0 = Debug|Any CPU
+               {02CFBD30-D77D-400F-94B2-700F60EFDD7F}.Release|Any CPU.ActiveCfg = Release|Any CPU
+               {02CFBD30-D77D-400F-94B2-700F60EFDD7F}.Release|Any CPU.Build.0 = Release|Any CPU
+               {1394E4CA-E17A-42F5-9216-8046ACA8D16B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+               {1394E4CA-E17A-42F5-9216-8046ACA8D16B}.Debug|Any CPU.Build.0 = Debug|Any CPU
+               {1394E4CA-E17A-42F5-9216-8046ACA8D16B}.Release|Any CPU.ActiveCfg = Release|Any CPU
+               {1394E4CA-E17A-42F5-9216-8046ACA8D16B}.Release|Any CPU.Build.0 = Release|Any CPU
+       EndGlobalSection
+       GlobalSection(SolutionProperties) = preSolution
+               HideSolutionNode = FALSE
+       EndGlobalSection
+EndGlobal
diff --git a/Hazel/Connection.cs b/Hazel/Connection.cs
new file mode 100644 (file)
index 0000000..7350be6
--- /dev/null
@@ -0,0 +1,128 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Net.Sockets;
+using System.Net;
+
+
+/* 
+* Copyright (C) Jamie Read - All Rights Reserved
+* Unauthorized copying of this file, via any medium is strictly prohibited
+* Proprietary and confidential
+* Written by Jamie Read <jamie.read@outlook.com>, January 2016
+*/
+
+namespace Hazel
+{
+    /// <summary>
+    ///     Handles the sending and receiving of messages through the channel to give connection orientated, packet based transmission.
+    /// </summary>
+    public abstract class Connection : IDisposable
+    {
+        /// <summary>
+        ///     Called when a message has been received.
+        /// </summary>
+        public event EventHandler<DataEventArgs> DataReceived;
+
+        /// <summary>
+        ///     Called when the end point disconnects from us or an error occurs.
+        /// </summary>
+        public event EventHandler<DisconnectedEventArgs> Disconnected;
+
+        /// <summary>
+        ///     The end point of this Connection.
+        /// </summary>
+        public ConnectionEndPoint EndPoint { get; protected set; }
+
+        /// <summary>
+        ///     The traffic statistics about this Connection.
+        /// </summary>
+        public ConnectionStatistics Statistics { get; protected set; }
+
+        /// <summary>
+        ///     The state of this connection.
+        /// </summary>
+        public ConnectionState State { get { return state; } protected set { state = value; } }
+        volatile ConnectionState state;
+
+        /// <summary>
+        ///     Constructor that initializes the ConnecitonStatistics object.
+        /// </summary>
+        protected Connection()
+        {
+            Statistics = new ConnectionStatistics();
+
+            State = ConnectionState.NotConnected;
+        }
+
+        /// <summary>
+        ///     Writes an array of bytes to the connection and prefixes the length.
+        /// </summary>
+        /// <param name="bytes">The bytes of the message to send.</param>
+        /// <param name="sendOption">The options this data is requested to send with.</param>
+        /// <remarks>
+        ///     The sendOptions parameter is only a request to use those options and the actual method used to send the
+        ///     data is up to the implementation. There are circumstances where this parameter may be ignored but in 
+        ///     general any implementer should aim to always follow the user's request here.
+        /// </remarks>
+        public abstract void WriteBytes(byte[] bytes, SendOption sendOption = SendOption.None);
+
+        /// <summary>
+        ///     Connects the connection to a remote server and begins listening.
+        /// </summary>
+        public abstract void Connect(ConnectionEndPoint remoteEndPoint);
+
+        /// <summary>
+        ///     Invokes the DataReceived event to alert subscribers we received data.
+        /// </summary>
+        /// <param name="args">The arguments to supply.</param>
+        protected void InvokeDataReceived(DataEventArgs args)
+        {
+            //Make a copy to avoid race condition between null check and invocation
+            EventHandler<DataEventArgs> handler = DataReceived;
+            if (handler != null)
+                handler(this, args);
+        }
+
+        /// <summary>
+        ///     Invokes the Disconnected event to alert hooked up methods there was an error or the remote end point disconnected.
+        /// </summary>
+        /// <param name="args">The arguments to supply.</param>
+        protected void InvokeDisconnected(DisconnectedEventArgs args)
+        {
+            //Make a copy to avoid race condition between null check and invocation
+            EventHandler<DisconnectedEventArgs> handler = Disconnected;
+            if (handler != null)
+                handler(this, args);
+        }
+
+        /// <summary>
+        ///     Closes this connections safely.
+        /// </summary>
+        public void Close()
+        {
+            Dispose();
+        }
+
+        /// <summary>
+        ///     Disposes of this NetworkConnection.
+        /// </summary>
+        public void Dispose()
+        {
+            Dispose(true);
+            GC.SuppressFinalize(this);
+        }
+
+        /// <summary>
+        ///     Disposes of this NetworkConnection.
+        /// </summary>
+        /// <param name="disposing">Are we currently disposing?</param>
+        protected virtual void Dispose(bool disposing)
+        {
+            if (disposing)
+            {
+            }
+        }
+    }
+}
diff --git a/Hazel/ConnectionEndPoint.cs b/Hazel/ConnectionEndPoint.cs
new file mode 100644 (file)
index 0000000..20a358f
--- /dev/null
@@ -0,0 +1,11 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Hazel
+{
+    public abstract class ConnectionEndPoint
+    {
+    }
+}
diff --git a/Hazel/ConnectionListener.cs b/Hazel/ConnectionListener.cs
new file mode 100644 (file)
index 0000000..640bf4c
--- /dev/null
@@ -0,0 +1,54 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Hazel
+{
+    /// <summary>
+    ///     Base class for all connection listeners.
+    /// </summary>
+    public abstract class ConnectionListener : IDisposable
+    {
+        /// <summary>
+        ///     Invoked when a new TCP connection is heard.
+        /// </summary>
+        public event EventHandler<NewConnectionEventArgs> NewConnection;
+
+        /// <summary>
+        ///     Makes this connection listener begin listening for connections.
+        /// </summary>
+        public abstract void Start();
+
+        /// <summary>
+        ///     Invokes the NewConnection event with the supplied args.
+        /// </summary>
+        /// <param name="args">The arguments for the event.</param>
+        protected void FireNewConnectionEvent(NewConnectionEventArgs args)
+        {
+            //Make a copy to avoid race condition between null check and invocation
+            EventHandler<NewConnectionEventArgs> handler = NewConnection;
+            if (handler != null)
+                handler(this, args);
+        }
+
+        /// <summary>
+        ///     Call to dispose of the connection listener.
+        /// </summary>
+        public void Dispose()
+        {
+            Dispose(true);
+        }
+
+        /// <summary>
+        ///     Called when the object is being disposed.
+        /// </summary>
+        /// <param name="disposing">Are we disposing?</param>
+        protected virtual void Dispose(bool disposing)
+        {
+
+        }
+    }
+}
diff --git a/Hazel/ConnectionState.cs b/Hazel/ConnectionState.cs
new file mode 100644 (file)
index 0000000..c8269bf
--- /dev/null
@@ -0,0 +1,33 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Hazel
+{
+    /// <summary>
+    ///     Marks the state a Connection is currently in.
+    /// </summary>
+    public enum ConnectionState
+    {
+        /// <summary>
+        ///     The Connection has either not been established yet or has been disconnected.
+        /// </summary>
+        NotConnected,
+        
+        /// <summary>
+        ///     The Connection is currently connecting to an endpoint.
+        /// </summary>
+        Connecting,
+
+        /// <summary>
+        ///     The Connection is connected and data can be transfered.
+        /// </summary>
+        Connected,
+
+        /// <summary>
+        ///     The Connection is currently disconnecting.
+        /// </summary>
+        Disconnecting
+    }
+}
diff --git a/Hazel/ConnectionStatistics.cs b/Hazel/ConnectionStatistics.cs
new file mode 100644 (file)
index 0000000..841c9d3
--- /dev/null
@@ -0,0 +1,135 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Hazel
+{
+    /// <summary>
+    ///     Holds statistics about the traffic through a Connection.
+    /// </summary>
+    public class ConnectionStatistics
+    {
+        /// <summary>
+        ///     The number of messages sent.
+        /// </summary>
+        public long MessagesSent
+        {
+            get
+            {
+                return Interlocked.Read(ref messagesSent);
+            }
+        }
+
+        /// <summary>
+        ///     The number of messages sent.
+        /// </summary>
+        long messagesSent;
+
+        /// <summary>
+        ///     The number of bytes of data sent.
+        /// </summary>
+        public long DataBytesSent
+        {
+            get
+            {
+                return Interlocked.Read(ref dataBytesSent);
+            }
+        }
+
+        /// <summary>
+        ///     The number of bytes of data sent.
+        /// </summary>
+        long dataBytesSent;
+
+        /// <summary>
+        ///     The number of bytes sent in total.
+        /// </summary>
+        public long TotalBytesSent
+        {
+            get
+            {
+                return Interlocked.Read(ref totalBytesSent);
+            }
+        }
+
+        /// <summary>
+        ///     The number of bytes sent in total.
+        /// </summary>
+        long totalBytesSent;
+
+        /// <summary>
+        ///     The number of messages received.
+        /// </summary>
+        public long MessagesReceived
+        {
+            get
+            {
+                return Interlocked.Read(ref messagesReceived);
+            }
+        }
+
+        /// <summary>
+        ///     The number of messages received.
+        /// </summary>
+        long messagesReceived;
+
+        /// <summary>
+        ///     The number of bytes of data received.
+        /// </summary>
+        public long DataBytesReceived
+        {
+            get
+            {
+                return Interlocked.Read(ref dataBytesReceived);
+            }
+        }
+
+        /// <summary>
+        ///     The number of bytes of data received.
+        /// </summary>
+        long dataBytesReceived;
+
+        /// <summary>
+        ///     The number of bytes received in total.
+        /// </summary>
+        public long TotalBytesReceived
+        {
+            get
+            {
+                return Interlocked.Read(ref totalBytesReceived);
+            }
+        }
+
+        /// <summary>
+        ///     The number of bytes received in total.
+        /// </summary>
+        long totalBytesReceived;
+
+        /// <summary>
+        ///     Logs the sending of a data packet in the statistics.
+        /// </summary>
+        /// <param name="dataLength">The number of bytes of data sent.</param>
+        /// <param name="totalLength">The total number of bytes sent.</param>
+        internal void LogSend(int dataLength, int totalLength)
+        {
+            Interlocked.Increment(ref messagesSent);
+            Interlocked.Add(ref dataBytesSent, dataLength);
+            Interlocked.Add(ref totalBytesSent, totalLength);
+        }
+
+        /// <summary>
+        ///     Logs the receiving of a data packet in the statistics.
+        /// </summary>
+        /// <param name="dataLength">The number of bytes of data received.</param>
+        /// <param name="totalLength">The total number of bytes received.</param>
+        internal void LogReceive(int dataLength, int totalLength)
+        {
+            Interlocked.Increment(ref messagesReceived);
+            Interlocked.Add(ref dataBytesReceived, dataLength);
+            Interlocked.Add(ref totalBytesReceived, totalLength);
+        }
+    }
+}
diff --git a/Hazel/DataEventArgs.cs b/Hazel/DataEventArgs.cs
new file mode 100644 (file)
index 0000000..a68e2fd
--- /dev/null
@@ -0,0 +1,31 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+/* 
+* Copyright (C) Jamie Read - All Rights Reserved
+* Unauthorized copying of this file, via any medium is strictly prohibited
+* Proprietary and confidential
+* Written by Jamie Read <jamie.read@outlook.com>, January 2016
+*/
+
+namespace Hazel
+{
+    public class DataEventArgs : EventArgs
+    {
+        /// <summary>
+        ///     The bytes received.
+        /// </summary>
+        public byte[] Bytes;
+
+        /// <summary>
+        ///     Creates DataEventArgs from bytes received.
+        /// </summary>
+        /// <param name="bytes"></param>
+        public DataEventArgs(byte[] bytes)
+        {
+            this.Bytes = bytes;
+        }
+    }
+}
diff --git a/Hazel/DisconnectedEventArgs.cs b/Hazel/DisconnectedEventArgs.cs
new file mode 100644 (file)
index 0000000..c4b9a0b
--- /dev/null
@@ -0,0 +1,27 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Hazel
+{
+    /// <summary>
+    ///     Events args for disconnected events.
+    /// </summary>
+    public class DisconnectedEventArgs
+    {
+        /// <summary>
+        ///     The exception, if any, that caused the disconnect, otherwise null.
+        /// </summary>
+        public Exception Exception { get; private set; }
+
+        /// <summary>
+        ///     Creates a DisconnectedEventArgs from the given exception or null
+        /// </summary>
+        /// <param name="e">The exception if the cause.</param>
+        internal DisconnectedEventArgs(Exception e)
+        {
+            this.Exception = e;
+        }
+    }
+}
diff --git a/Hazel/Hazel.csproj b/Hazel/Hazel.csproj
new file mode 100644 (file)
index 0000000..dcad336
--- /dev/null
@@ -0,0 +1,82 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProjectGuid>{02CFBD30-D77D-400F-94B2-700F60EFDD7F}</ProjectGuid>
+    <OutputType>Library</OutputType>
+    <AppDesignerFolder>Properties</AppDesignerFolder>
+    <RootNamespace>Hazel</RootNamespace>
+    <AssemblyName>Hazel</AssemblyName>
+    <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
+    <FileAlignment>512</FileAlignment>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>bin\Debug\</OutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <DebugType>pdbonly</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>bin\Release\</OutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <PropertyGroup>
+    <SignAssembly>true</SignAssembly>
+  </PropertyGroup>
+  <PropertyGroup>
+    <AssemblyOriginatorKeyFile>Hazel.snk</AssemblyOriginatorKeyFile>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="System" />
+    <Reference Include="System.Core" />
+    <Reference Include="System.Xml.Linq" />
+    <Reference Include="System.Data.DataSetExtensions" />
+    <Reference Include="Microsoft.CSharp" />
+    <Reference Include="System.Data" />
+    <Reference Include="System.Xml" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="Connection.cs" />
+    <Compile Include="ConnectionEndPoint.cs" />
+    <Compile Include="ConnectionListener.cs" />
+    <Compile Include="ConnectionState.cs" />
+    <Compile Include="DataEventArgs.cs" />
+    <Compile Include="DisconnectedEventArgs.cs" />
+    <Compile Include="HazelException.cs" />
+    <Compile Include="NetworkEndPoint.cs" />
+    <Compile Include="NewConnectionEventArgs.cs" />
+    <Compile Include="Properties\AssemblyInfo.cs" />
+    <Compile Include="SendFlags.cs" />
+    <Compile Include="StateObject.cs" />
+    <Compile Include="ConnectionStatistics.cs" />
+    <Compile Include="TcpConnection.cs" />
+    <Compile Include="TcpConnectionListener.cs" />
+    <Compile Include="UdpClientConnection.cs" />
+    <Compile Include="UdpConnection.cs">
+      <SubType>Code</SubType>
+    </Compile>
+    <Compile Include="UdpConnectionListener.cs" />
+    <Compile Include="UdpServerConnection.cs" />
+    <Compile Include="Utility.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="Hazel.snk" />
+  </ItemGroup>
+  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
+       Other similar extension points exist, see Microsoft.Common.targets.
+  <Target Name="BeforeBuild">
+  </Target>
+  <Target Name="AfterBuild">
+  </Target>
+  -->
+</Project>
\ No newline at end of file
diff --git a/Hazel/Hazel.snk b/Hazel/Hazel.snk
new file mode 100644 (file)
index 0000000..3a4a4f0
Binary files /dev/null and b/Hazel/Hazel.snk differ
diff --git a/Hazel/HazelException.cs b/Hazel/HazelException.cs
new file mode 100644 (file)
index 0000000..86f157b
--- /dev/null
@@ -0,0 +1,23 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Hazel
+{
+    /// <summary>
+    ///     Wrapper for exceptions thrown from Hazel.
+    /// </summary>
+    class HazelException : Exception
+    {
+        internal HazelException(string msg) : base (msg)
+        {
+
+        }
+
+        internal HazelException(string msg, System.Net.Sockets.SocketException e) : base (msg, e)
+        {
+
+        }
+    }
+}
diff --git a/Hazel/NetworkEndPoint.cs b/Hazel/NetworkEndPoint.cs
new file mode 100644 (file)
index 0000000..66947d7
--- /dev/null
@@ -0,0 +1,50 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+using System.Net;
+
+namespace Hazel
+{
+    /// <summary>
+    ///     Represents an endpoint to a remote resource on a network.
+    /// </summary>
+    public class NetworkEndPoint : ConnectionEndPoint
+    {
+        /// <summary>
+        ///     The EndPoint this points to.
+        /// </summary>
+        public EndPoint EndPoint { get; set; }
+
+        /// <summary>
+        ///     Creates a NetworkEndPoint from a given EndPoint.
+        /// </summary>
+        /// <param name="endPoint">The endpoint we represent./param>
+        public NetworkEndPoint(EndPoint endPoint)
+        {
+            this.EndPoint = endPoint;
+        }
+
+        /// <summary>
+        ///     Create a NetworkEndPoint to the specified address and port.
+        /// </summary>
+        /// <param name="address">The IP address of the server.</param>
+        /// <param name="port">The port the server is listening on.</param>
+        public NetworkEndPoint(IPAddress address, int port) : this(new IPEndPoint(address, port))
+        {
+
+        }
+
+        /// <summary>
+        ///     Creates a NetworkEndPoint to the specified IP address and port.
+        /// </summary>
+        /// <param name="IP">A valid IP address of the server.</param>
+        /// <param name="port">The port the server is listening on.</param>
+        public NetworkEndPoint(string IP, int port) : this(IPAddress.Parse(IP), port)
+        {
+
+        }
+    }
+}
diff --git a/Hazel/NewConnectionEventArgs.cs b/Hazel/NewConnectionEventArgs.cs
new file mode 100644 (file)
index 0000000..e4b6ab8
--- /dev/null
@@ -0,0 +1,23 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Hazel
+{
+    /// <summary>
+    ///     Event args for new connection events.
+    /// </summary>
+    public class NewConnectionEventArgs : EventArgs
+    {
+        /// <summary>
+        ///     The new connection.
+        /// </summary>
+        public Connection Connection { get; private set; }
+
+        internal NewConnectionEventArgs(Connection Connection)
+        {
+            this.Connection = Connection;
+        }
+    }
+}
diff --git a/Hazel/Properties/AssemblyInfo.cs b/Hazel/Properties/AssemblyInfo.cs
new file mode 100644 (file)
index 0000000..cfed1cb
--- /dev/null
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following 
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("Hazel")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("Hazel")]
+[assembly: AssemblyCopyright("Copyright ©  2016")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible 
+// to COM components.  If you need to access a type in this assembly from 
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("f3935f38-a904-40c7-ab9b-8d01aefe0489")]
+
+// Version information for an assembly consists of the following four values:
+//
+//      Major Version
+//      Minor Version 
+//      Build Number
+//      Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers 
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/Hazel/SendFlags.cs b/Hazel/SendFlags.cs
new file mode 100644 (file)
index 0000000..27251ff
--- /dev/null
@@ -0,0 +1,55 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Hazel
+{
+    /// <summary>
+    ///     Specifies how a message should be sent.
+    /// </summary>
+    [Flags]
+    public enum SendOption : byte
+    {
+        /// <summary>
+        ///     Requests unreliable delivery with no framentation or ordering.
+        /// </summary>
+        None = 0,
+
+        /// <summary>
+        ///     Requests data be sent reliably. Data is guaranteed to arrive at it's destination.
+        /// </summary>
+        Reliable = 1,
+
+        /// <summary>
+        ///     Requests that data should be sent in order.
+        /// </summary>
+        /// <remarks>
+        ///     Any packets that are out of order in this option will be dropped.
+        /// </remarks>
+        Ordered = 2,
+
+        /// <summary>
+        ///     Requests that data should be sent in order and reliably.
+        /// </summary>
+        /// <remarks>
+        ///     Only messages that are sent using OrderedReliable or OrderedFragmentedReliable will arrive
+        ///     in order, other messages
+        ///     may arrive in between.
+        /// </remarks>
+        OrderedReliable = 3,
+
+        /// <summary>
+        ///     Requests data be sent so that large messages are fragmented into smaller chunks of
+        ///     data and reassembled when received.
+        /// </summary>
+        FragmentedReliable = 5,
+
+        /// <summary>
+        ///     Requests data be sent so that large messages are fragmented into smaller chunks of data and
+        ///     reassembled when received and that the message arrives in order with other messages.
+        /// </summary>
+        OrderedFragmentedReliable = 7
+    }
+}
diff --git a/Hazel/StateObject.cs b/Hazel/StateObject.cs
new file mode 100644 (file)
index 0000000..f1fc1c3
--- /dev/null
@@ -0,0 +1,40 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Hazel
+{
+    /// <summary>
+    ///     Represents the state of the current receive operation for TCP and Pipe connections.
+    /// </summary>
+    public struct StateObject
+    {
+        /// <summary>
+        ///     The buffer we're receiving.
+        /// </summary>
+        internal byte[] buffer;
+
+        /// <summary>
+        ///     The total number of bytes received so far.
+        /// </summary>
+        internal int totalBytesReceived;
+
+        /// <summary>
+        ///     The callback to invoke once the buffer has been filled.
+        /// </summary>
+        internal Action<byte[]> callback;
+
+        /// <summary>
+        ///     Creates a StateObject with the specified length.
+        /// </summary>
+        /// <param name="length">The number of bytes expected to be received.</param>
+        internal StateObject(int length, Action<byte[]> callback)
+        {
+            this.buffer = new byte[length];
+            this.totalBytesReceived = 0;
+            this.callback = callback;
+        }
+    }
+}
diff --git a/Hazel/TcpConnection.cs b/Hazel/TcpConnection.cs
new file mode 100644 (file)
index 0000000..5843491
--- /dev/null
@@ -0,0 +1,308 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net;
+using System.Net.Sockets;
+using System.Text;
+
+/* 
+* Copyright (C) Jamie Read - All Rights Reserved
+* Unauthorized copying of this file, via any medium is strictly prohibited
+* Proprietary and confidential
+* Written by Jamie Read <jamie.read@outlook.com>, January 2016
+*/
+
+namespace Hazel
+{
+    /// <summary>
+    ///     Represents a connection that uses the TCP protocol.
+    /// </summary>
+    public class TcpConnection : Connection
+    {
+        /// <summary>
+        ///     The socket we're managing.
+        /// </summary>
+        public Socket Socket { get; private set; }
+
+        /// <summary>
+        ///     The remote end point of this connection.
+        /// </summary>
+        public EndPoint RemoteEndPoint { get; protected set; }
+
+        /// <summary>
+        ///     Creates a TcpConnection from a given TCP Socket.
+        /// </summary>
+        /// <param name="socket"></param>
+        internal TcpConnection(Socket socket)
+        {
+            //Check it's a TCP socket
+            if (socket.ProtocolType != System.Net.Sockets.ProtocolType.Tcp)
+                throw new ArgumentException("A TcpConnection requires a TCP socket.");
+
+            this.EndPoint = new NetworkEndPoint(socket.RemoteEndPoint);
+            this.RemoteEndPoint = socket.RemoteEndPoint;
+
+            this.Socket = socket;
+
+            lock (this.Socket)
+            {
+                this.Socket.NoDelay = true;
+            }
+
+            State = ConnectionState.Connected;
+        }
+
+        /// <summary>
+        ///     Creates a new TCP connection.
+        /// </summary>
+        public TcpConnection()
+        {
+            //Create and connect a socket
+            Socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, System.Net.Sockets.ProtocolType.Tcp);
+
+            Socket.NoDelay = true;
+        }
+
+        /// <summary>
+        ///     Connects this TCP connection to the endpoint.
+        /// </summary>
+        /// <param name="remotEndPoint">The location of the server to connect to.</param>
+        public override void Connect(ConnectionEndPoint remoteEndPoint)
+        {
+            NetworkEndPoint nep = remoteEndPoint as NetworkEndPoint;
+            if (nep == null)
+            {
+                throw new ArgumentException("The remote end point of a TCP connection must be a NetworkEndPoint.");
+            }
+            
+            this.EndPoint = remoteEndPoint;
+            this.RemoteEndPoint = nep.EndPoint;
+
+            //Connect
+            lock (Socket)
+            {
+                if (State != ConnectionState.NotConnected)
+                    throw new InvalidOperationException("Cannot connect as the Connection is already connected.");
+
+                State = ConnectionState.Connecting;
+
+                try
+                {
+                    Socket.Connect(nep.EndPoint);
+                }
+                catch (SocketException e)
+                {
+                    throw new HazelException("Could not connect as a socket exception occured.", e);
+                }
+            }
+
+            //Start receiving data
+            try
+            {
+                StartWaitingForHeader();
+            }
+            catch (SocketException e)
+            {
+                throw new HazelException("A Socket exception occured while initiating a receive operation.", e);
+            }
+
+            //Set connected
+            lock (Socket)
+                State = ConnectionState.Connected;
+        }
+
+        /// <summary>
+        ///     Writes an array of bytes to the connection and prefixes the length.
+        /// </summary>
+        /// <param name="bytes">The bytes of the message to send.</param>
+        /// <param name="sendOption">The options this data is requested to send with.</param>
+        /// <remarks>
+        ///     The sendOptions parameter is ignored by the TcpConnection as TCP only supports OrderedFragmentedReliable communication.
+        /// </remarks>
+        public override void WriteBytes(byte[] bytes, SendOption sendOption = SendOption.OrderedFragmentedReliable)
+        {
+            //Get bytes for length
+            byte[] fullBytes = Utility.AppendLengthHeader(bytes);
+
+            //Write the bytes to the socket
+            lock (Socket)
+            {
+                if (State != ConnectionState.Connected)
+                    throw new InvalidOperationException("Could not send data as this Connection is not connected. Did you disconnect?");
+            
+                try
+                {
+                    Socket.BeginSend(fullBytes, 0, fullBytes.Length, SocketFlags.None, null, null);
+                }
+                catch (SocketException e)
+                {
+                    HazelException he = new HazelException("Could not send data as a SocketException occured.", e);
+                    HandleDisconnect(he);
+                    throw he;
+                }
+            }
+
+            Statistics.LogSend(bytes.Length, fullBytes.Length);
+        }
+
+        /// <summary>
+        ///     Called when a 4 byte header has been received.
+        /// </summary>
+        /// <param name="result">The result of the async operation.</param>
+        protected virtual void HeaderReadCallback(byte[] bytes)
+        {
+            //Get length 
+            int length = Utility.GetLengthFromBytes(bytes);
+
+            //Begin receiving the body
+            try
+            {
+                StartWaitingForBytes(length, BodyReadCallback);
+            }
+            catch (SocketException e)
+            {
+                HandleDisconnect(new HazelException("A Socket exception occured while initiating a receive operation.", e));
+            }
+        }
+
+        /// <summary>
+        ///     Callback for when a body has been read.
+        /// </summary>
+        /// <param name="result"></param>
+        protected virtual void BodyReadCallback(byte[] bytes)
+        {
+            //Begin receiving from the start
+            StartWaitingForHeader();
+
+            Statistics.LogReceive(bytes.Length, bytes.Length + 4);
+
+            //Fire DataReceived event
+            InvokeDataReceived(new DataEventArgs(bytes));
+        }
+
+        /// <summary>
+        ///     Starts this connections waiting for the header.
+        /// </summary>
+        protected void StartWaitingForHeader()
+        {
+            StartWaitingForBytes(4, HeaderReadCallback);
+        }
+
+        /// <summary>
+        ///     Waits for the specified amount of bytes to be received.
+        /// </summary>
+        /// <param name="length">The number of bytes to receive.</param>
+        /// <param name="callback">The callback </param>
+        protected virtual void StartWaitingForBytes(int length, Action<byte[]> callback)
+        {
+            StateObject state = new StateObject(length, callback);
+
+            StartWaitingForChunk(state);
+        }
+
+        /// <summary>
+        ///     Waits for the next chunk of data from this socket.
+        /// </summary>
+        /// <param name="state">The StateObject for the receive operation.</param>
+        protected virtual void StartWaitingForChunk(StateObject state)
+        {
+            lock (Socket)
+                Socket.BeginReceive(state.buffer, state.totalBytesReceived, state.buffer.Length, SocketFlags.None, ChunkReadCallback, state);
+        }
+
+        /// <summary>
+        ///     Called when a chunk has been read.
+        /// </summary>
+        /// <param name="result"></param>
+        protected virtual void ChunkReadCallback(IAsyncResult result)
+        {
+            int bytesReceived;
+
+            //End the receive operation
+            try
+            {
+                lock (Socket)
+                    bytesReceived = Socket.EndReceive(result);
+            }
+            catch (ObjectDisposedException)
+            {
+                //If the socket's been disposed then we can just end there.
+                return;
+            }
+
+            StateObject state = (StateObject)result.AsyncState;
+
+            state.totalBytesReceived += bytesReceived;
+
+            //Exit if receive nothing
+            if (bytesReceived == 0)
+            {
+                HandleDisconnect();
+                return;
+            }
+
+            //If we need to receive more then wait for more, else process it.
+            if (state.totalBytesReceived < state.buffer.Length)
+            {
+                try
+                {
+                    StartWaitingForChunk(state);
+                }
+                catch (SocketException e)
+                {
+                    HandleDisconnect(new HazelException("A Socket exception occured while initiating a receive operation.", e));
+                    return;
+                }
+            }
+            else
+                state.callback.Invoke(state.buffer);
+        }
+
+        /// <summary>
+        ///     Called when the socket has been disconnected at the remote host.
+        /// </summary>
+        /// <param name="e">The exception if one was the cause.</param>
+        void HandleDisconnect(HazelException e = null)
+        {
+            bool invoke = false;
+
+            lock (Socket)
+            {
+                //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)
+            {
+                InvokeDisconnected(new DisconnectedEventArgs(e));
+
+                Dispose();
+            }
+        }
+
+        /// <summary>
+        ///     Closes this connections safely.
+        /// </summary>
+        protected override void Dispose(bool disposing)
+        {
+            if (disposing)
+            {
+                lock (Socket)
+                {
+                    State = ConnectionState.NotConnected;
+
+                    if (Socket.Connected)
+                        Socket.Shutdown(SocketShutdown.Send);
+                    Socket.Dispose();
+                }
+            }
+
+            base.Dispose(disposing);
+        }
+    }
+}
diff --git a/Hazel/TcpConnectionListener.cs b/Hazel/TcpConnectionListener.cs
new file mode 100644 (file)
index 0000000..1c01ad6
--- /dev/null
@@ -0,0 +1,119 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net;
+using System.Net.Sockets;
+using System.Text;
+using System.Threading.Tasks;
+
+/* 
+* Copyright (C) Jamie Read - All Rights Reserved
+* Unauthorized copying of this file, via any medium is strictly prohibited
+* Proprietary and confidential
+* Written by Jamie Read <jamie.read@outlook.com>, January 2016
+*/
+
+namespace Hazel
+{
+    /// <summary>
+    ///     Listens for new TCP connections and creates TCPConnections for them.
+    /// </summary>
+    public class TcpConnectionListener : ConnectionListener
+    {
+        /// <summary>
+        ///     The IP address we're listening on.
+        /// </summary>
+        public IPAddress IPAddress { get; private set; }
+
+        /// <summary>
+        ///     The port we're listening on.
+        /// </summary>
+        public int Port { get; private set; }
+
+        /// <summary>
+        ///     The socket listening for connections.
+        /// </summary>
+        public Socket Listener { get; private set; }
+
+        /// <summary>
+        ///     Creates a new ConnectionListener for the given IP and port.
+        /// </summary>
+        /// <param name="ipAdress">The IPAddress to listen on.</param>
+        /// <param name="port">The port to listen on.</param>
+        public TcpConnectionListener(IPAddress IPAddress, int port)
+        {
+            this.IPAddress = IPAddress;
+            this.Port = port;
+
+            this.Listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
+        }
+
+        /// <summary>
+        ///     Makes this connection listener begin listening for connections.
+        /// </summary>
+        public override void Start()
+        {
+            try
+            {
+                lock (Listener)
+                {
+                    Listener.Bind(new IPEndPoint(IPAddress, Port));
+                    Listener.Listen(1000);
+
+                    Listener.BeginAccept(AcceptConnection, null);
+                }
+            }
+            catch (SocketException e)
+            {
+                throw new HazelException("Could not start listening as a SocketException occured", e);
+            }
+        }
+
+        /// <summary>
+        ///     Called when a new connection has been accepted by the listener.
+        /// </summary>
+        /// <param name="result">The asyncronous operation's result.</param>
+        void AcceptConnection(IAsyncResult result)
+        {
+            lock (Listener)
+            {
+                //Accept Tcp socket
+                Socket tcpSocket;
+                try
+                {
+                    tcpSocket = Listener.EndAccept(result);
+                }
+                catch (ObjectDisposedException)
+                {
+                    //If the socket's been disposed then we can just end there.
+                    return;
+                }
+
+                //Start listening for the next connection
+                Listener.BeginAccept(new AsyncCallback(AcceptConnection), null);
+
+                //Sort the event out
+                TcpConnection tcpConnection = new TcpConnection(tcpSocket);
+
+                NewConnectionEventArgs args = new NewConnectionEventArgs(tcpConnection);
+
+                FireNewConnectionEvent(args);
+            }
+        }
+
+        /// <summary>
+        ///     Called when the object is being disposed.
+        /// </summary>
+        /// <param name="disposing">Are we being disposed?</param>
+        protected override void Dispose(bool disposing)
+        {
+            if (disposing)
+            {
+                lock (Listener)
+                    Listener.Dispose();
+            }
+
+            base.Dispose(disposing);
+        }
+    }
+}
diff --git a/Hazel/UdpClientConnection.cs b/Hazel/UdpClientConnection.cs
new file mode 100644 (file)
index 0000000..f9fc59b
--- /dev/null
@@ -0,0 +1,232 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net;
+using System.Net.Sockets;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Hazel
+{
+    public class UdpClientConnection : UdpConnection
+    {
+        /// <summary>
+        ///     The socket we're connected via.
+        /// </summary>
+        Socket socket;
+
+        /// <summary>
+        ///     The buffer to store incomming data in.
+        /// </summary>
+        byte[] dataBuffer = new byte[ushort.MaxValue];
+
+        /// <summary>
+        ///     Creates a new UdpClientConnection.
+        /// </summary>
+        public UdpClientConnection()
+        {
+            socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
+        }
+
+        /// <summary>
+        ///     Writes an array of bytes to the connection.
+        /// </summary>
+        /// <param name="bytes">The bytes of the message to send.</param>
+        /// <param name="sendOption">The option this data is requested to send with.</param>
+        public override void WriteBytes(byte[] bytes, SendOption sendOption = SendOption.None)
+        {
+            //Add sendflag byte to start
+            byte[] fullBytes = new byte[bytes.Length + 1];
+            fullBytes[0] = (byte)sendOption;
+            Buffer.BlockCopy(bytes, 0, fullBytes, 1, bytes.Length);
+
+            //Pack
+            SocketAsyncEventArgs args = new SocketAsyncEventArgs();
+            args.SetBuffer(fullBytes, 0, fullBytes.Length);
+            args.RemoteEndPoint = RemoteEndPoint;
+
+            lock (socket)
+            {
+                if (State != ConnectionState.Connected)
+                    throw new InvalidOperationException("Could not send data as this Connection is not connected. Did you disconnect?");
+
+                try
+                {
+                    socket.SendToAsync(args);
+                }
+                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 e)
+                {
+                    HazelException he = new HazelException("Could not send data as a SocketException occured.", e);
+                    HandleDisconnect(he);
+                    throw he;
+                }
+            }
+
+            Statistics.LogSend(bytes.Length, fullBytes.Length);
+        }
+
+        /// <summary>
+        ///     Connects this Connection to a given remote server and begins listening for data.
+        /// </summary>
+        public override void Connect(ConnectionEndPoint remoteEndPoint)
+        {
+            NetworkEndPoint nep = remoteEndPoint as NetworkEndPoint;
+            if (nep == null)
+            {
+                throw new ArgumentException("The remote end point of a TCP connection must be a NetworkEndPoint.");
+            }
+
+            this.EndPoint = nep;
+            this.RemoteEndPoint = nep.EndPoint;
+
+            lock (socket)
+            {
+                if (State != ConnectionState.NotConnected)
+                    throw new InvalidOperationException("Cannot connect as the Connection is already connected.");
+
+                State = ConnectionState.Connecting;
+
+                //Begin listening
+                try
+                {
+                    //TODO should that really be IPAddress.Any?
+                    socket.Bind(new IPEndPoint(IPAddress.Any, 0));
+                }
+                catch (SocketException e)
+                {
+                    throw new HazelException("A socket exception occured while binding to the port.", e);
+                }
+
+                try
+                {
+                    StartListeningForData();
+                }
+                catch (ObjectDisposedException)
+                {
+                    throw new HazelException("Could not begin read as the socket has been disposed of, did you disconnect?");
+                }
+                catch (SocketException e)
+                {
+                    throw new HazelException("A Socket exception occured while initiating a receive operation.", e);
+                }
+
+                State = ConnectionState.Connected;
+            }
+
+            //Write bytes to the server to tell it hi (and to punch a hole in our NAT, if present).
+            WriteBytes(new byte[] { 0 }, SendOption.Reliable);
+        }
+
+        /// <summary>
+        ///     Instructs the listener to begin listening.
+        /// </summary>
+        void StartListeningForData()
+        {
+            socket.BeginReceive(dataBuffer, 0, dataBuffer.Length, SocketFlags.None, ReadCallback, dataBuffer);
+        }
+
+        /// <summary>
+        ///     Called when data has been received by the socket.
+        /// </summary>
+        /// <param name="result">The asyncronous operation's result.</param>
+        void ReadCallback(IAsyncResult result)
+        {
+            int bytesReceived;
+
+            //End the receive operation
+            try
+            {
+                lock (socket)
+                    bytesReceived = socket.EndReceive(result);
+            }
+            catch (ObjectDisposedException)
+            {
+                //If the socket's been disposed then we can just end there.
+                return;
+            }
+            catch (SocketException e)
+            {
+                HandleDisconnect(new HazelException("A socket exception occured while reading data.", e));
+                return;
+            }
+
+            //Exit if no bytes read, we've failed.
+            if (bytesReceived == 0)
+            {
+                HandleDisconnect();
+                return;
+            }
+
+            //Copy to new buffer
+            byte[] buffer = new byte[bytesReceived];
+            Buffer.BlockCopy((byte[])result.AsyncState, 1, buffer, 0, bytesReceived - 1);
+
+            //Begin receiving again
+            try
+            {
+                lock (socket)
+                    StartListeningForData();
+            }
+            catch (SocketException e)
+            {
+                HandleDisconnect(new HazelException("A Socket exception occured while initiating a receive operation.", e));
+            }
+
+            Statistics.LogReceive(buffer.Length - 1, buffer.Length);
+
+            InvokeDataReceived(new DataEventArgs(buffer));
+        }
+
+        /// <summary>
+        ///     Called when the socket has been disconnected at the remote host.
+        /// </summary>
+        /// <param name="e">The exception if one was the cause.</param>
+        void HandleDisconnect(HazelException e = null)
+        {
+            bool invoke = false;
+
+            lock (socket)
+            {
+                //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)
+            {
+                InvokeDisconnected(new DisconnectedEventArgs(e));
+
+                Dispose();
+            }
+        }
+
+        /// <summary>
+        ///     Safely closes this connection.
+        /// </summary>
+        protected override void Dispose(bool disposing)
+        {
+            //Dispose of the socket
+            if (disposing)
+            {
+                lock (socket)
+                {
+                    State = ConnectionState.NotConnected;
+
+                    socket.Dispose();
+                }
+            }
+
+            base.Dispose(disposing);
+        }
+    }
+}
diff --git a/Hazel/UdpConnection.cs b/Hazel/UdpConnection.cs
new file mode 100644 (file)
index 0000000..5c1b9ed
--- /dev/null
@@ -0,0 +1,90 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net;
+using System.Net.Sockets;
+using System.Text;
+using System.Threading;
+
+/* 
+* Copyright (C) Jamie Read - All Rights Reserved
+* Unauthorized copying of this file, via any medium is strictly prohibited
+* Proprietary and confidential
+* Written by Jamie Read <jamie.read@outlook.com>, January 2016
+*/
+
+namespace Hazel
+{
+    /// <summary>
+    ///     Represents a connection that uses the UDP protocol.
+    /// </summary>
+    public abstract class UdpConnection : Connection
+    {
+        /// <summary>
+        ///     The packets of data that have been transmitted reliably and not acknowledged.
+        /// </summary>
+        Dictionary<uint, Packet> reliableDataPacketsSent = new Dictionary<uint, Packet>();
+
+        /// <summary>
+        ///     Holds the last ID allocated.
+        /// </summary>
+        volatile uint lastIDAllocated;
+
+        /// <summary>
+        ///     The remote end point of this connection.
+        /// </summary>
+        public EndPoint RemoteEndPoint { get; protected set; }
+
+        class Packet
+        {
+            public byte[] Data;
+            public DateTime SentTime;
+
+            public Packet(byte[] data, DateTime sentTime)
+            {
+                Data = data;
+                SentTime = sentTime;
+            }
+        }
+
+        /// <summary>
+        ///     Handles the reliable/fragmented/ordered sending from this connection.
+        /// </summary>
+        /// <param name="data">The data being sent.</param>
+        /// <param name="sendOption">The send option.</param>
+        /// <returns>The bytes that should actually be sent.</returns>
+        protected byte[] HandleSend(byte[] data, SendOption sendOption)
+        {
+            byte[] bytes = new byte[data.Length + 1];
+            int offset = 1;
+
+            if (sendOption == SendOption.Reliable)
+            {
+                bytes = new byte[data.Length + 5];
+                offset = 5;
+
+                lock (reliableDataPacketsSent)
+                {
+                    //Find an ID not used yet.
+                    uint id;
+
+                    do
+                        id = ++lastIDAllocated;
+                    while (reliableDataPacketsSent.ContainsKey(id));
+
+                    bytes[1] = (byte)(id & 0xFF);
+                    bytes[2] = (byte)((id >> 16) & 0xFF);
+                    bytes[3] = (byte)((id >> 8) & 0xFF);
+                    bytes[4] = (byte)id;
+
+                    //Remember packet
+                    reliableDataPacketsSent.Add(id, new Packet(data, DateTime.Now));
+                }
+            }
+
+            Buffer.BlockCopy(data, 0, bytes, offset, bytes.Length);
+
+            return bytes;
+        }
+    }
+}
diff --git a/Hazel/UdpConnectionListener.cs b/Hazel/UdpConnectionListener.cs
new file mode 100644 (file)
index 0000000..a0151f2
--- /dev/null
@@ -0,0 +1,199 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net;
+using System.Net.Sockets;
+using System.Text;
+using System.Threading.Tasks;
+//TODO complete trawl through for thread safety, everywhere
+/* 
+* Copyright (C) Jamie Read - All Rights Reserved
+* Unauthorized copying of this file, via any medium is strictly prohibited
+* Proprietary and confidential
+* Written by Jamie Read <jamie.read@outlook.com>, January 2016
+*/
+
+namespace Hazel
+{
+    /// <summary>
+    ///     Listens for new UDP connections and creates UdpConnection for them.
+    /// </summary>
+    public class UdpConnectionListener : ConnectionListener
+    {
+        /// <summary>
+        ///     The IP address we're listening on.
+        /// </summary>
+        public IPAddress IPAddress { get; private set; }
+
+        /// <summary>
+        ///     The port we're listening on.
+        /// </summary>
+        public int Port { get; private set; }
+
+        /// <summary>
+        ///     The socket listening for connections.
+        /// </summary>
+        Socket listener;
+
+        /// <summary>
+        ///     The connections we currently hold
+        /// </summary>
+        Dictionary<EndPoint, UdpServerConnection> connections = new Dictionary<EndPoint, UdpServerConnection>();
+
+        /// <summary>
+        ///     Creates a new ConnectionListener for the given IP and port.
+        /// </summary>
+        /// <param name="ipAdress">The IPAddress to listen on.</param>
+        /// <param name="port">The port to listen on.</param>
+        public UdpConnectionListener(IPAddress IPAddress, int port)
+        {
+            this.IPAddress = IPAddress;
+            this.Port = port;
+
+            this.listener = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
+        }
+
+        /// <summary>
+        ///     Instruct the listener to begin listening for connections.
+        /// </summary>
+        public override void Start()
+        {
+            try
+            {
+                lock (listener)
+                    listener.Bind(new IPEndPoint(IPAddress, Port));
+            }
+            catch (SocketException e)
+            {
+                throw new HazelException("Could not start listening as a SocketException occured", e);
+            }
+
+            StartListeningForData();
+        }
+
+        /// <summary>
+        ///     Instructs the listener to begin listening.
+        /// </summary>
+        void StartListeningForData()
+        {
+            EndPoint remoteEP = new IPEndPoint(IPAddress.Any, 0);
+            byte[] dataBuffer = new byte[ushort.MaxValue];
+
+            try
+            {
+                lock (listener)
+                    listener.BeginReceiveFrom(dataBuffer, 0, dataBuffer.Length, SocketFlags.None, ref remoteEP, ReadCallback, dataBuffer);
+            }
+            catch (ObjectDisposedException)
+            {
+                return;
+            }
+        }
+
+        /// <summary>
+        ///     Called when data has been received by the listener.
+        /// </summary>
+        /// <param name="result">The asyncronous operation's result.</param>
+        void ReadCallback(IAsyncResult result)
+        {
+            int bytesReceived;
+            EndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, 0);
+
+            //End the receive operation
+            try
+            {
+                lock (listener) //TODO how does this stop when the client disconnects?
+                    bytesReceived = listener.EndReceiveFrom(result, ref remoteEndPoint);
+            }
+            catch (ObjectDisposedException)
+            {
+                //If the socket's been disposed then we can just end there.
+                return;
+            }
+            catch (SocketException e)
+            {
+                //TODO Errr...;
+                return;
+            }
+
+            //Exit if no bytes read, we've closed.
+            if (bytesReceived == 0)
+                return;
+
+            //Copy to new buffer
+            byte[] buffer = new byte[bytesReceived];
+            Buffer.BlockCopy((byte[])result.AsyncState, 0, buffer, 0, bytesReceived);
+
+            //Begin receiving again
+            StartListeningForData();
+
+            //If we're aware of this connection pass the data to the neccesary UdpConnection
+            bool exists;
+            lock (connections)
+                exists = connections.ContainsKey(remoteEndPoint);
+            
+            if (exists)
+            {
+                lock (connections)
+                    connections[remoteEndPoint].InvokeDataReceived(buffer);
+            }
+            //If this is a new client then connect with them!
+            else
+            {
+                UdpServerConnection newConnection = new UdpServerConnection(this, remoteEndPoint);
+                lock (connections)
+                    connections.Add(remoteEndPoint, newConnection);
+
+                //And tell everyone about it!
+                FireNewConnectionEvent(new NewConnectionEventArgs(newConnection));
+            }
+        }
+
+        /// <summary>
+        ///     Sends data from the listener socket.
+        /// </summary>
+        /// <param name="bytes">The bytes to send.</param>
+        /// <param name="endPoint">The endpoint to send to.</param>
+        internal void SendData(byte[] bytes, EndPoint endPoint)
+        {
+            SocketAsyncEventArgs args = new SocketAsyncEventArgs();
+            args.SetBuffer(bytes, 0, bytes.Length);
+            args.RemoteEndPoint = endPoint;
+
+            try
+            {
+                lock (listener)
+                    listener.SendToAsync(args);
+            }
+            catch (SocketException e)
+            {
+                throw new HazelException("Could not send data as a SocketException occured.", e);
+            }
+        }
+
+        /// <summary>
+        ///     Removes a virtual connection from the list.
+        /// </summary>
+        /// <param name="endPoint">The endpoint of the virtual connection.</param>
+        internal void RemoveConnectionTo(EndPoint endPoint)
+        {
+            lock (connections)
+                connections.Remove(endPoint);
+        }
+
+        /// <summary>
+        ///     Called when the listener is being disposed of
+        /// </summary>
+        /// <param name="disposing"></param>
+        protected override void Dispose(bool disposing)
+        {
+            if (disposing)
+            {
+                lock (listener)
+                    listener.Dispose();
+            }
+
+            base.Dispose(disposing);
+        }
+    }
+}
diff --git a/Hazel/UdpServerConnection.cs b/Hazel/UdpServerConnection.cs
new file mode 100644 (file)
index 0000000..ebf255f
--- /dev/null
@@ -0,0 +1,109 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net;
+using System.Text;
+using System.Threading.Tasks;
+
+/* 
+* Copyright (C) Jamie Read - All Rights Reserved
+* Unauthorized copying of this file, via any medium is strictly prohibited
+* Proprietary and confidential
+* Written by Jamie Read <jamie.read@outlook.com>, January 2016
+*/
+
+namespace Hazel
+{
+    class UdpServerConnection : UdpConnection
+    {
+        /// <summary>
+        ///     The connection listener that we use the socket of.
+        /// </summary>
+        public UdpConnectionListener Listener { get; private set; }
+
+        /// <summary>
+        ///     Lock object for the writing to the state of the connection.
+        /// </summary>
+        Object stateLock = new Object();
+
+        /// <summary>
+        ///     Creates a UdpConnection for the virtual connection to the endpoint.
+        /// </summary>
+        /// <param name="socket"></param>
+        internal UdpServerConnection(UdpConnectionListener listener, EndPoint endPoint)
+        {
+            this.Listener = listener;
+            this.RemoteEndPoint = endPoint;
+            this.EndPoint = new NetworkEndPoint(endPoint);
+
+            State = ConnectionState.Connected;
+        }
+
+        /// <summary>
+        ///     Writes an array of bytes to the connection.
+        /// </summary>
+        /// <param name="bytes">The bytes of the message to send.</param>
+        /// <param name="sendOption">The option this data is requested to send with.</param>
+        public override void WriteBytes(byte[] bytes, SendOption sendOption = SendOption.None)
+        {
+            //Add sendflag byte to start
+            byte[] fullBytes = new byte[bytes.Length + 1];
+            fullBytes[0] = (byte)sendOption;
+            Buffer.BlockCopy(bytes, 0, fullBytes, 1, bytes.Length);
+
+            lock (stateLock)
+            {
+                if (State != ConnectionState.Connected)
+                    throw new InvalidOperationException("Could not send data as this Connection is not connected. Did you disconnect?");
+
+                Listener.SendData(fullBytes, RemoteEndPoint);
+            }
+
+            Statistics.LogSend(bytes.Length, fullBytes.Length);
+        }
+
+        /// <summary>
+        ///     Connects this Connection to a given remote server.
+        /// </summary>
+        /// <remarks>
+        ///     This will always throw an InvalidOperationException.
+        /// </remarks>
+        public override void Connect(ConnectionEndPoint remoteEndPoint)
+        {
+            throw new InvalidOperationException("Cannot manually connect a UdpServerConnection, did you mean to use UdpClientConnection?");
+        }
+
+        /// <summary>
+        ///     Called by the listener when we have data.
+        /// </summary>
+        /// <param name="buffer"></param>
+        internal void InvokeDataReceived(byte[] buffer)
+        {
+            byte[] data = new byte[buffer.Length - 1];
+            Buffer.BlockCopy(buffer, 1, data, 0, data.Length);
+
+            Statistics.LogReceive(data.Length, buffer.Length); 
+            
+            InvokeDataReceived(new DataEventArgs(data));
+        }
+
+        /// <summary>
+        ///     Safely closes this connection.
+        /// </summary>
+        protected override void Dispose(bool disposing)
+        {
+            //Here we just need to inform the listener we no longer need data.
+            if (disposing)
+            {
+                lock (stateLock)
+                {
+                    Listener.RemoveConnectionTo(RemoteEndPoint);
+
+                    State = ConnectionState.NotConnected;
+                }
+            }
+
+            base.Dispose(disposing);
+        }
+    }
+}
diff --git a/Hazel/Utility.cs b/Hazel/Utility.cs
new file mode 100644 (file)
index 0000000..98357cc
--- /dev/null
@@ -0,0 +1,45 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Hazel
+{
+    class Utility
+    {
+        /// <summary>
+        ///     Appends the length header to the bytes.
+        /// </summary>
+        /// <param name="bytes">The source bytes.</param>
+        /// <returns></returns>
+        internal static byte[] AppendLengthHeader(byte[] bytes)
+        {
+            byte[] fullBytes = new byte[bytes.Length + 4];
+
+            //Append length
+            fullBytes[0] = (byte)(((uint)bytes.Length >> 24) & 0xFF);
+            fullBytes[1] = (byte)(((uint)bytes.Length >> 16) & 0xFF);
+            fullBytes[2] = (byte)(((uint)bytes.Length >> 8) & 0xFF);
+            fullBytes[3] = (byte)(uint)bytes.Length;
+
+            //Add rest of bytes
+            Buffer.BlockCopy(bytes, 0, fullBytes, 4, bytes.Length);
+
+            return fullBytes;
+        }
+
+        /// <summary>
+        ///     Returns the length from a length header.
+        /// </summary>
+        /// <param name="bytes"></param>
+        /// <returns></returns>
+        internal static int GetLengthFromBytes(byte[] bytes)
+        {
+            if (bytes.Length < 4)
+                throw new IndexOutOfRangeException("Not enough bytes passed to calculate length.");
+
+            return (bytes[0] << 24) | (bytes[1] << 16) | (bytes[2] << 8) | bytes[3];
+        }
+    }
+}