]> git.deb.at Git - rhonda/impostor.hazel.git/commitdiff
Added documentation and IPv6 support
authorJamJar00 <jamster.30@btinternet.com>
Sun, 29 May 2016 14:03:33 +0000 (15:03 +0100)
committerJamJar00 <jamster.30@btinternet.com>
Sun, 29 May 2016 14:03:33 +0000 (15:03 +0100)
31 files changed:
Hazel/Connection.cs
Hazel/ConnectionEndPoint.cs
Hazel/ConnectionListener.cs
Hazel/ConnectionState.cs
Hazel/ConnectionStatistics.cs
Hazel/DataEventArgs.cs
Hazel/DisconnectedEventArgs.cs
Hazel/DocInclude/TcpClientExample.cs [new file with mode: 0644]
Hazel/DocInclude/UdpClientExample.cs [new file with mode: 0644]
Hazel/DocInclude/UdpListenerExample.cs [new file with mode: 0644]
Hazel/DocInclude/common.xml [new file with mode: 0644]
Hazel/Hazel.csproj
Hazel/Hazel.snk [deleted file]
Hazel/IRecyclable.cs
Hazel/NetworkConnection.cs
Hazel/NetworkConnectionListener.cs
Hazel/NetworkEndPoint.cs
Hazel/NewConnectionEventArgs.cs
Hazel/ObjectPool.cs
Hazel/SendOption.cs
Hazel/SendOptionInternal.cs
Hazel/StateObject.cs
Hazel/TcpConnection.cs
Hazel/TcpConnectionListener.cs
Hazel/UdpClientConnection.cs
Hazel/UdpConnection.KeepAlive.cs
Hazel/UdpConnection.Reliable.cs
Hazel/UdpConnection.cs
Hazel/UdpConnectionListener.cs
Hazel/UdpServerConnection.cs
Hazel/Utility.cs [deleted file]

index 3418f63ac2929d6ca8649040e2da21a66f9d4ced..1f6731201c648ff5b56fa81e0fe6535786da0892 100644 (file)
@@ -6,44 +6,105 @@ using System.Net.Sockets;
 using System.Net;
 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>
-    ///     Handles the sending and receiving of messages through the channel to give connection orientated, packet based transmission.
+    ///     Base class for all connections.
     /// </summary>
+    /// <remarks>
+    ///     <para>
+    ///         Connection is the base class for all connections that Hazel can make. It provides common functionality and a 
+    ///         standard interface to allow connections to be swapped easily.
+    ///     </para>
+    ///     <para>
+    ///         Any class inheriting from Connection should provide the 3 standard guarantees that Hazel provides:
+    ///         <list type="bullet">
+    ///             <item>
+    ///                 <description>Thread Safe</description>
+    ///             </item>
+    ///             <item>
+    ///                 <description>Connection Orientated</description>
+    ///             </item>
+    ///             <item>
+    ///                 <description>Packet/Message Based</description>
+    ///             </item>
+    ///         </list>
+    ///     </para>
+    /// </remarks>
+    /// <threadsafety static="true" instance="true"/>
     public abstract class Connection : IDisposable
     {
         /// <summary>
         ///     Called when a message has been received.
         /// </summary>
+        /// <remarks>
+        ///     <para>
+        ///         DataReceived is invoked everytime a message is received from the end point of this connection, the message 
+        ///         that was received can be found in the <see cref="DataEventArgs"/> alongside other information from the 
+        ///         event.
+        ///     </para>
+        ///     <include file="DocInclude/common.xml" path="docs/item[@name='Event_Thread_Safety_Warning']/*" />
+        /// </remarks>
+        /// <example>
+        ///     <code language="C#" source="DocInclude/TcpClientExample.cs"/>
+        /// </example>
         public event EventHandler<DataEventArgs> DataReceived;
 
         /// <summary>
-        ///     Called when the end point disconnects from us or an error occurs.
+        ///     Called when the end point disconnects or an error occurs.
         /// </summary>
+        /// <remarks>
+        ///     <para>
+        ///         Disconnected is invoked when the connection is closed due to an exception occuring or because the remote 
+        ///         end point disconnected. If it was invoked due to an exception occuring then the exception is available 
+        ///         in the <see cref="DisconnectedEventArgs"/> passed with the event.
+        ///     </para>
+        ///     <include file="DocInclude/common.xml" path="docs/item[@name='Event_Thread_Safety_Warning']/*" />
+        /// </remarks>
+        /// <example>
+        ///     <code language="C#" source="DocInclude/TcpClientExample.cs"/>
+        /// </example>
         public event EventHandler<DisconnectedEventArgs> Disconnected;
 
         /// <summary>
-        ///     The end point of this Connection.
+        ///     The remote end point of this Connection.
         /// </summary>
+        /// <remarks>
+        ///     This is the end point that this connection is connected to (i.e. the other device). This returns an abstract 
+        ///     <see cref="ConnectionEndPoint"/> which can then be cast to an appropriate end point depending on the 
+        ///     connection type.
+        /// </remarks>
         public ConnectionEndPoint EndPoint { get; protected set; }
 
         /// <summary>
         ///     The traffic statistics about this Connection.
         /// </summary>
+        /// <remarks>
+        ///     Contains statistics about the number of messages and bytes sent and received by this connection.
+        /// </remarks>
         public ConnectionStatistics Statistics { get; protected set; }
 
         /// <summary>
         ///     The state of this connection.
         /// </summary>
+        /// <remarks>
+        ///     <para>
+        ///         Connections go round 4 states in their lifetime: they start as <see cref="ConnectionState.NotConnected"/> to 
+        ///         indicate they have no endpoint, calling <see cref="Connect"/> takes them into 
+        ///         <see cref="ConnectionState.Connecting"/>, once they have received confirmation they are connected they enter
+        ///         <see cref="ConnectionState.Connected"/> and finally calling <see cref="Close"/> sets them to 
+        ///         <see cref="ConnectionState.Disconnecting"/> and then the sequence repeats back to
+        ///         <see cref="ConnectionState.NotConnected"/> once disconnection is complete.
+        ///     </para>
+        ///     <para>
+        ///         Data can only be sent while in <see cref="ConnectionState.Connected"/> and all attempts to send data when
+        ///         in any other state will throw an InvalidOperationException.
+        ///     </para>
+        ///     <para>
+        ///         All implementers should be aware that when this is set to <see cref="ConnectionState.Connected"/> it will
+        ///         release all threads that are blocked on <see cref="WaitOnConnect"/>.
+        ///     </para>
+        /// </remarks>
         public ConnectionState State
         {
             get
@@ -71,6 +132,10 @@ namespace Hazel
         /// <summary>
         ///     Constructor that initializes the ConnecitonStatistics object.
         /// </summary>
+        /// <remarks>
+        ///     This constructor initialises <see cref="Statistics"/> with empty statistics and sets <see cref="State"/> to 
+        ///     <see cref="ConnectionState.NotConnected"/>.
+        /// </remarks>
         protected Connection()
         {
             Statistics = new ConnectionStatistics();
@@ -79,27 +144,40 @@ namespace Hazel
         }
 
         /// <summary>
-        ///     Writes an array of bytes to the connection and prefixes the length.
+        ///     Sends a number of bytes to the end point of the connection using the specified <see cref="SendOption"/>.
         /// </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>
+        /// <param name="sendOption">The option specifying how the message should be sent.</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.
+        ///     <include file="DocInclude/common.xml" path="docs/item[@name='Connection_SendBytes_General']/*" />
+        ///     <para>
+        ///         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.
+        ///     </para>
         /// </remarks>
-        public abstract void WriteBytes(byte[] bytes, SendOption sendOption = SendOption.None);
+        public abstract void SendBytes(byte[] bytes, SendOption sendOption = SendOption.None);
 
         /// <summary>
-        ///     Connects the connection to a remote server and begins listening.
+        ///     Connects the connection to a server and begins listening.
         /// </summary>
+        /// <remarks>
+        ///     Calling Connect makes the connection attempt to connect to the end point that's specified in the 
+        ///     <see cref="ConnectionEndPoint"/> passed. This method will block until the connection attempt completes and
+        ///     will throw a <see cref="HazelException"/> if there is a problem connecting.
+        /// </remarks>
         public abstract void Connect(ConnectionEndPoint remoteEndPoint);
 
         /// <summary>
-        ///     Invokes the DataReceived event to alert subscribers we received data.
+        ///     Invokes the DataReceived event.
         /// </summary>
-        /// <param name="bytes">The bytes to supply.</param>
-        /// <param name="sendOption">The sendOption to supply.</param>
+        /// <param name="bytes">The bytes received.</param>
+        /// <param name="sendOption">The <see cref="SendOption"/> the message was received with.</param>
+        /// <remarks>
+        ///     Invokes the <see cref="DataReceived"/> event on this connection to alert subscribers a new message has been
+        ///     received. The bytes and the send option that the message was sent with should be passed in to give to the
+        ///     subscribers.
+        /// </remarks>
         protected void InvokeDataReceived(byte[] bytes, SendOption sendOption)
         {
             DataEventArgs args = DataEventArgs.GetObject();
@@ -112,10 +190,15 @@ namespace Hazel
         }
 
         /// <summary>
-        ///     Invokes the Disconnected event to alert hooked up methods there was an error or the remote end point disconnected.
+        ///     Invokes the Disconnected event.
         /// </summary>
         /// <param name="e">The exception, if any, that occured to cause this.</param>
-        protected void InvokeDisconnected(Exception e)
+        /// <remarks>
+        ///     Invokes the <see cref="Disconnected"/> event to alert subscribres this connection has been disconnected either 
+        ///     by the end point or because an error occured. If an error occured the error should be passed in in order to 
+        ///     pass to the subscribers, otherwise null can be passed in.
+        /// </remarks>
+        protected void InvokeDisconnected(Exception e = null)
         {
             DisconnectedEventArgs args = DisconnectedEventArgs.GetObject();
             args.Set(e);
@@ -129,6 +212,11 @@ namespace Hazel
         /// <summary>
         ///     Blocks until the Connection is connected.
         /// </summary>
+        /// <remarks>
+        ///     This is a helper method for waiting until the connection is connected. It will block until the 
+        ///     <see cref="State"/> property is set to <see cref="ConnectionState.Connected"/> allowing the main thread to 
+        ///     wait until specific data is received etc. before returning to the user's code.
+        /// </remarks>
         protected void WaitOnConnect()
         {
             connectWaitLock.WaitOne();
@@ -137,6 +225,17 @@ namespace Hazel
         /// <summary>
         ///     Closes this connection safely.
         /// </summary>
+        /// <remarks>
+        ///     <para>
+        ///         Informs the end point of the connection that we are disconnecting from them and disposes of this 
+        ///         connection.
+        ///     </para>
+        ///     <para>
+        ///         This calls <see cref="Dispose"/> and therefore sets <see cref="State"/> straight to 
+        ///         <see cref="ConnectionState.NotConnected"/>. Once you call Close you will not be able to send any more
+        ///         data using this connection and no more data will be received.
+        ///     </para> 
+        /// </remarks>
         public virtual void Close()
         {
             Dispose();
index 20a358f6129b76aee8611dedde68727e7b8bb158..3e095379f4f93b8617fddcabd250e11338cc86ee 100644 (file)
@@ -5,6 +5,10 @@ using System.Text;
 
 namespace Hazel
 {
+    /// <summary>
+    ///     Base class for all end points of connections.
+    /// </summary>
+    /// <threadsafety static="true" instance="true"/>
     public abstract class ConnectionEndPoint
     {
     }
index 60452c4eefab46108fc7ffbeb3309ed0a7fa5b1b..e738f1bfb81722714e265e0427a56caf5d2d9c5f 100644 (file)
@@ -10,22 +10,69 @@ namespace Hazel
     /// <summary>
     ///     Base class for all connection listeners.
     /// </summary>
+    /// <remarks>
+    ///     <para>
+    ///         ConnectionListeners are server side objects that listen for clients and create matching server side connections 
+    ///         for each client in a similar way to TCP does. These connections should already have a 
+    ///         <see cref="Connection.State">State</see> of <see cref="ConnectionState.Connected"/> and so should be ready for 
+    ///         comunication immediately.
+    ///     </para>
+    ///     <para>
+    ///         Each time a client connects the <see cref="NewConnection"/> event will be invoked to alert all subscribers to
+    ///         the new connection. A disconnected event is then present on the <see cref="Connection"/> that is passed to the
+    ///         subscribers.
+    ///     </para>
+    /// </remarks>
+    /// <threadsafety static="true" instance="true"/>
     public abstract class ConnectionListener : IDisposable
     {
         /// <summary>
-        ///     Invoked when a new TCP connection is heard.
+        ///     Invoked when a new client connects.
         /// </summary>
+        /// <remarks>
+        ///     <para>
+        ///         NewConnection is invoked each time a client connects to the listener. The 
+        ///         <see cref="NewConnectionEventArgs"/> contains the new <see cref="Connection"/> for communication with this
+        ///         client.
+        ///     </para>
+        ///     <para>
+        ///         Hazel doesn't store connections so it is your responsibility to keep track of the connections to your 
+        ///         server. Note that as <see cref="Connection"/> implements <see cref="IDisposable"/> if you are not storing
+        ///         a connection then as a bare minimum you should call <see cref="Connection.Dispose"/> here in order to 
+        ///         release the connection correctly.
+        ///     </para>
+        ///     <include file="DocInclude/common.xml" path="docs/item[@name='Event_Thread_Safety_Warning']/*" />
+        /// </remarks>
+        /// <example>
+        ///     <code language="C#" source="DocInclude/TcpListenerExample.cs"/>
+        /// </example>
         public event EventHandler<NewConnectionEventArgs> NewConnection;
-
+        //TODO add threadsafe markers on all xmldocs
         /// <summary>
         ///     Makes this connection listener begin listening for connections.
         /// </summary>
+        /// <remarks>
+        ///     <para>
+        ///         This instructs the listener to begin listening for new clients connecting to the server. When a new client 
+        ///         connects the <see cref="NewConnection"/> event will be invoked containing the connection to the new client.
+        ///     </para>
+        ///     <para>
+        ///         To stop listening you should call <see cref="Dispose"/>.
+        ///     </para>
+        /// </remarks>
+        /// <example>
+        ///     <code language="C#" source="DocInclude/TcpListenerExample.cs"/>
+        /// </example>
         public abstract void Start();
 
         /// <summary>
-        ///     Invokes the NewConnection event with the supplied args.
+        ///     Invokes the NewConnection event with the supplied connection.
         /// </summary>
-        /// <param name="args">The arguments for the event.</param>
+        /// <param name="args">The connection to pass to subscribers.</param>
+        /// <remarks>
+        ///     Implementers should call this to invoke the <see cref="NewConnection"/> event before data is received so that
+        ///     subscribers do not miss any data that may have been sent immediately after connecting.
+        /// </remarks>
         protected void InvokeNewConnection(Connection connection)
         {
             //Get new args
index c8269bf2d212a9ecd853cbb9f3973a3859532995..5036b90bf13a4743c6b82fa131aa33c5379c94ac 100644 (file)
@@ -6,7 +6,7 @@ using System.Text;
 namespace Hazel
 {
     /// <summary>
-    ///     Marks the state a Connection is currently in.
+    ///     Represents the state a <see cref="Connection"/> is currently in.
     /// </summary>
     public enum ConnectionState
     {
index 841c9d3198d75bc8775c72e7bf91a3119eef056b..450c3d6c6c57e3a2fd9c4646d1c0f38ab8acf26c 100644 (file)
@@ -8,13 +8,19 @@ using System.Threading.Tasks;
 namespace Hazel
 {
     /// <summary>
-    ///     Holds statistics about the traffic through a Connection.
+    ///     Holds statistics about the traffic through a <see cref="Connection"/>.
     /// </summary>
+    /// <threadsafety static="true" instance="true"/>
     public class ConnectionStatistics
     {
         /// <summary>
         ///     The number of messages sent.
         /// </summary>
+        /// <remarks>
+        ///     This is the number of messages that were sent from the <see cref="Connection"/>, incremented each time that 
+        ///     LogSend is called by the Connection. Messages that caused an error are not counted and messages are only 
+        ///     counted once all other operations in the send are complete.
+        /// </remarks>
         public long MessagesSent
         {
             get
@@ -31,6 +37,16 @@ namespace Hazel
         /// <summary>
         ///     The number of bytes of data sent.
         /// </summary>
+        /// <remarks>
+        ///     <para>
+        ///         This is the number of bytes of data (i.e. user bytes) that were sent from the <see cref="Connection"/>, 
+        ///         accumulated each time that LogSend is called by the Connection. Messages that caused an error are not 
+        ///         counted and messages are only counted once all other operations in the send are complete.
+        ///     </para>
+        ///     <para>
+        ///         For the number of bytes including protocol bytes see <see cref="TotalBytesSent"/>.
+        ///     </para>
+        /// </remarks>
         public long DataBytesSent
         {
             get
@@ -47,6 +63,17 @@ namespace Hazel
         /// <summary>
         ///     The number of bytes sent in total.
         /// </summary>
+        /// <remarks>
+        ///     <para>
+        ///         This is the total number of bytes (the data bytes plus protocol bytes) that were sent from the 
+        ///         <see cref="Connection"/>, accumulated each time that LogSend is called by the Connection. Messages that 
+        ///         caused an error are not counted and messages are only counted once all other operations in the send are 
+        ///         complete.
+        ///     </para>
+        ///     <para>
+        ///         For the number of data bytes excluding protocol bytes see <see cref="DataBytesSent"/>.
+        ///     </para>
+        /// </remarks>
         public long TotalBytesSent
         {
             get
@@ -63,6 +90,10 @@ namespace Hazel
         /// <summary>
         ///     The number of messages received.
         /// </summary>
+        /// <remarks>
+        ///     This is the number of messages that were received by the <see cref="Connection"/>, incremented each time that 
+        ///     LogReceive is called by the Connection. Messages are counted before the receive event is invoked.
+        /// </remarks>
         public long MessagesReceived
         {
             get
@@ -79,6 +110,16 @@ namespace Hazel
         /// <summary>
         ///     The number of bytes of data received.
         /// </summary>
+        /// <remarks>
+        ///     <para>
+        ///         This is the number of bytes of data (i.e. user bytes) that were received by the <see cref="Connection"/>, 
+        ///         accumulated each time that LogReceive is called by the Connection. Messages are counted before the receive
+        ///         event is invoked.
+        ///     </para>
+        ///     <para>
+        ///         For the number of bytes including protocol bytes see <see cref="TotalBytesReceived"/>.
+        ///     </para>
+        /// </remarks>
         public long DataBytesReceived
         {
             get
@@ -95,6 +136,16 @@ namespace Hazel
         /// <summary>
         ///     The number of bytes received in total.
         /// </summary>
+        /// <remarks>
+        ///     <para>
+        ///         This is the total number of bytes (the data bytes plus protocol bytes) that were received by the 
+        ///         <see cref="Connection"/>, accumulated each time that LogReceive is called by the Connection. Messages are 
+        ///         counted before the receive event is invoked.
+        ///     </para>
+        ///     <para>
+        ///         For the number of data bytes excluding protocol bytes see <see cref="DataBytesReceived"/>.
+        ///     </para>
+        /// </remarks>
         public long TotalBytesReceived
         {
             get
@@ -113,6 +164,9 @@ namespace Hazel
         /// </summary>
         /// <param name="dataLength">The number of bytes of data sent.</param>
         /// <param name="totalLength">The total number of bytes sent.</param>
+        /// <remarks>
+        ///     This should be called after the data has been sent and should only be called for data that is sent sucessfully.
+        /// </remarks>
         internal void LogSend(int dataLength, int totalLength)
         {
             Interlocked.Increment(ref messagesSent);
@@ -125,6 +179,9 @@ namespace Hazel
         /// </summary>
         /// <param name="dataLength">The number of bytes of data received.</param>
         /// <param name="totalLength">The total number of bytes received.</param>
+        /// <remarks>
+        ///     This should be called before the received event is invoked so it is up to date for subscribers to that event.
+        /// </remarks>
         internal void LogReceive(int dataLength, int totalLength)
         {
             Interlocked.Increment(ref messagesReceived);
index a1cb0592356fa77ae3820d6944b59724dfde3b1a..fd56a6b1190ef9d46ad9c93b64830e02d87a533f 100644 (file)
@@ -3,15 +3,18 @@ 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
 {
+    /// <summary>
+    ///     Event arguments for the <see cref="Connection.DataEvent"/> event.
+    /// </summary>
+    /// <remarks>
+    ///     <para>
+    ///         This contains information about messages received by a connection and is passed to subscribers of the 
+    ///         <see cref="Connection.DataEvent">DataEvent</see>. 
+    ///     </para>
+    ///     <include file="DocInclude/common.xml" path="docs/item[@name='Recyclable']/*" />
+    /// </remarks>
     public class DataEventArgs : EventArgs, IRecyclable
     {
         /// <summary>
@@ -22,19 +25,19 @@ namespace Hazel
         /// <summary>
         ///     Returns an instance of this object from the pool.
         /// </summary>
-        /// <returns></returns>
+        /// <returns>A new or recycled DataEventArgs object.</returns>
         internal static DataEventArgs GetObject()
         {
             return objectPool.GetObject();
         }
 
         /// <summary>
-        ///     The bytes received.
+        ///     The bytes received from the client.
         /// </summary>
         public byte[] Bytes { get; private set; }
 
         /// <summary>
-        ///     The SendOption the data was sent with.
+        ///     The <see cref="SendOption"/> the data was sent with.
         /// </summary>
         public object SendOption { get; private set; }
 
@@ -57,9 +60,7 @@ namespace Hazel
             this.SendOption = sendOption;
         }
 
-        /// <summary>
-        ///     Returns this object back to the object pool.
-        /// </summary>
+        /// <inheritdoc />
         public void Recycle()
         {
             objectPool.PutObject(this);
index 4468eef94b15abbdeca658478dd5566dfec8c60b..86d26c5b43b7b443ecea06054fc4027af2c75db0 100644 (file)
@@ -6,8 +6,15 @@ using System.Text;
 namespace Hazel
 {
     /// <summary>
-    ///     Events args for disconnected events.
+    ///     Event arguments for the <see cref="Connection.Disconnected"/> event.
     /// </summary>
+    /// <remarks>
+    ///     <para>
+    ///         This contains information about the cause of a disconnection and is passed to subscribers of the
+    ///         <see cref="Connection.Disconnected"/> event.
+    ///     </para>
+    ///     <include file="DocInclude/common.xml" path="docs/item[@name='Recyclable']/*" />
+    /// </remarks>
     public class DisconnectedEventArgs : IRecyclable
     {
         /// <summary>
@@ -18,15 +25,20 @@ namespace Hazel
         /// <summary>
         ///     Returns an instance of this object from the pool.
         /// </summary>
-        /// <returns></returns>
+        /// <returns>A new or recycled DisconnectedEventArgs object.</returns>
         internal static DisconnectedEventArgs GetObject()
         {
             return objectPool.GetObject();
         }
 
         /// <summary>
-        ///     The exception, if any, that caused the disconnect, otherwise null.
+        ///     The exception, if any, that caused the disconnect.
         /// </summary>
+        /// <remarks>
+        ///     If the disconnection was caused because of an exception occuring (for exemple a <see cref="SocketException"/> 
+        ///     on network based connections) this will contain the error that caused it or a <see cref="HazelException"/> 
+        ///     with the details of the exception, if the disconnection wasn't caused by an error then this will contain null.
+        /// </remarks>
         public Exception Exception { get; private set; }
 
         /// <summary>
@@ -46,9 +58,7 @@ namespace Hazel
             this.Exception = e;
         }
 
-        /// <summary>
-        ///     Returns this object back to the object pool.
-        /// </summary>
+        /// <inheritdoc />
         public void Recycle()
         {
             objectPool.PutObject(this);
diff --git a/Hazel/DocInclude/TcpClientExample.cs b/Hazel/DocInclude/TcpClientExample.cs
new file mode 100644 (file)
index 0000000..cc2bdc2
--- /dev/null
@@ -0,0 +1,24 @@
+class TcpClientExample
+{
+    static void Main(string[] args)
+    {
+        using (TcpConnection connection = new TcpConnection())
+        {
+            ManualResetEvent e = new ManualResetEvent(false);
+
+            //Whenever we receive data print the number of bytes and how it was sent
+            connection.DataReceived += (object sender, DataEventArgs a) =>
+                Console.WriteLine("Received {0} bytes via {1}!", a.Bytes.Length, a.SendOption);
+
+            //When the end point disconnects from us then release the main thread and exit
+            connection.Disconnected += (object sender, DisconnectedEventArgs a) =>
+                e.Set();
+
+            //Connect to a server
+            connection.Connect(new NetworkEndPoint("127.0.0.1", 4296));
+
+            //Wait until the end point disconnects from us
+            e.WaitOne();
+        }
+    }
+}
diff --git a/Hazel/DocInclude/UdpClientExample.cs b/Hazel/DocInclude/UdpClientExample.cs
new file mode 100644 (file)
index 0000000..b576f26
--- /dev/null
@@ -0,0 +1,24 @@
+class UdpClientExample
+{
+    static void Main(string[] args)
+    {
+        using (UdpConnection connection = new UdpConnection())
+        {
+            ManualResetEvent e = new ManualResetEvent(false);
+
+            //Whenever we receive data print the number of bytes and how it was sent
+            connection.DataReceived += (object sender, DataEventArgs a) =>
+                Console.WriteLine("Received {0} bytes via {1}!", a.Bytes.Length, a.SendOption);
+
+            //When the end point disconnects from us then release the main thread and exit
+            connection.Disconnected += (object sender, DisconnectedEventArgs a) =>
+                e.Set();
+
+            //Connect to a server
+            connection.Connect(new NetworkEndPoint("127.0.0.1", 4296));
+
+            //Wait until the end point disconnects from us
+            e.WaitOne();
+        }
+    }
+}
diff --git a/Hazel/DocInclude/UdpListenerExample.cs b/Hazel/DocInclude/UdpListenerExample.cs
new file mode 100644 (file)
index 0000000..73c2432
--- /dev/null
@@ -0,0 +1,23 @@
+class UdpListenerExample
+{
+    static void Main(string[] args)
+    {
+        //Setup listener
+        using (UdpConnectionListener listener = new UdpConnectionListener(IPAddress.Any, 4296))
+        {
+            //Start listening for new connection events
+            listener.NewConnection += delegate(object sender, NewConnectionEventArgs a)
+            {
+                //Send the client some data
+                a.Connection.SendBytes(new byte[] { 0, 1, 2, 3, 4, 5, 6, 7 }, SendOption.Reliable);
+
+                //Disconnect from the client
+                a.Connection.Close();
+            };
+
+            listener.Start();
+
+            Console.ReadKey();
+        }
+    }
+}
diff --git a/Hazel/DocInclude/common.xml b/Hazel/DocInclude/common.xml
new file mode 100644 (file)
index 0000000..76edc64
--- /dev/null
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8" ?>
+
+<docs>
+  <item name="Event_Thread_Safety_Warning">
+    <para>
+      As with all Hazel events it is invoked on a thread from the .NET <see cref="ThreadPool"/> and hence any
+      subscribers should ensure their handling code is thread safe. Implementing connections are not bound to
+      invoking this event in the sequence messages are received, in fact implementers are only required to
+      ensure this method is always and only invoked for a user sent message, therefore subscribers should be
+      aware that this event may be called out of order and may be called whilst another thread is still handling
+      an invocation of the event.
+    </para>
+  </item>
+  <item name="Recyclable">
+    <para>
+      This object implements IRecyclable and hence can be recycled in order to reduce the number of objects the 
+      GC has to deal with. When you are done with the object you can either leave it unreferenced as you usually 
+      would and the GC will collect it or you can call <see cref="Recycle"/> to inform Hazel that the object 
+      should be reused. Once recycle has been called the contents can be overwritten at any time and so only 
+      call it once you are completely finished with the object.
+    </para>
+  </item>
+  <item name="Connection_SendBytes_General">
+    <para>
+      This method sends a number of bytes in a message to the end point of this client using the given
+      <see cref="SendOption"/> to describe how the data should be sent. Sending messages requires that the
+      this connection is connected to a remote end point and SendBytes will throw an exception if that is not
+      the case. See the <see cref="State"/> property for information on whether a connection is connected or not.
+    </para>
+  </item>
+</docs>
\ No newline at end of file
index 9547f6480408f08ce8007729696935f4763ef6d2..ce4bf630ca3d36fa7434c16a1a65ed6f818b5813 100644 (file)
@@ -36,7 +36,8 @@
     <SignAssembly>true</SignAssembly>
   </PropertyGroup>
   <PropertyGroup>
-    <AssemblyOriginatorKeyFile>Hazel.snk</AssemblyOriginatorKeyFile>
+    <AssemblyOriginatorKeyFile>
+    </AssemblyOriginatorKeyFile>
   </PropertyGroup>
   <ItemGroup>
     <Reference Include="System" />
     <Compile Include="ConnectionState.cs" />
     <Compile Include="DataEventArgs.cs" />
     <Compile Include="DisconnectedEventArgs.cs" />
+    <None Include="DocInclude\TcpClientExample.cs" />
+    <None Include="DocInclude\UdpClientExample.cs" />
+    <None Include="DocInclude\TcpListenerExample.cs" />
+    <None Include="DocInclude\UdpListenerExample.cs" />
     <Compile Include="HazelException.cs" />
+    <Compile Include="IPMode.cs" />
     <Compile Include="IRecyclable.cs" />
     <Compile Include="NetworkConnection.cs" />
     <Compile Include="NetworkConnectionListener.cs" />
     <Compile Include="UdpConnection.Reliable.cs" />
     <Compile Include="UdpConnectionListener.cs" />
     <Compile Include="UdpServerConnection.cs" />
-    <Compile Include="Utility.cs" />
   </ItemGroup>
   <ItemGroup>
-    <None Include="Hazel.snk" />
+    <Content Include="DocInclude\common.xml">
+      <SubType>Designer</SubType>
+    </Content>
   </ItemGroup>
   <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
   <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
diff --git a/Hazel/Hazel.snk b/Hazel/Hazel.snk
deleted file mode 100644 (file)
index 3a4a4f0..0000000
Binary files a/Hazel/Hazel.snk and /dev/null differ
index c044f3c8c487c2fc22cb82fb781296376ecc4cac..3dd1d4c352cad76c92acdb2fe6722cc76b17daca 100644 (file)
@@ -13,6 +13,16 @@ namespace Hazel
         /// <summary>
         ///     Returns this object back to the object pool.
         /// </summary>
+        /// <remarks>
+        ///     <para>
+        ///         Calling this when you are done with the object returns the object back to a pool in order to be reused.
+        ///         This can reduce the amount of work the GC has to do dramatically but it is optional to call this.
+        ///     </para>
+        ///     <para>
+        ///         Calling this indicates to Hazel that this can be reused and thus you should only call this when you are
+        ///         completely finished with the object as the contents can be overwritten at any point after.
+        ///     </para>
+        /// </remarks>
         void Recycle();
     }
 }
index 72800002647661f30824ca4a3e42f7097d6452c7..2363a015ce24fa303b7a810cd7c5d0e8d006bab0 100644 (file)
@@ -5,23 +5,20 @@ 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
 {
     /// <summary>
-    ///     A connection to a remote end point via a network protocol.
+    ///     Abstract base class for a <see cref="Connection"/> to a remote end point via a network protocol like TCP or UDP.
     /// </summary>
     public abstract class NetworkConnection : Connection
     {
         /// <summary>
         ///     The remote end point of this connection.
         /// </summary>
+        /// <remarks>
+        ///     This is the end point of the other device given as an <see cref="System.Net.EndPoint"/> rather than a generic
+        ///     <see cref="ConnectionEndPoint"/> as the base <see cref="Connection"/> does.
+        /// </remarks>
         public EndPoint RemoteEndPoint { get; protected set; }
     }
 }
index db52d691c8ba6423a49d38d376f4336147933726..4cb46a4bd7abd4bd7be49cac4f5da93601801dd4 100644 (file)
@@ -5,27 +5,20 @@ 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
 {
     /// <summary>
-    ///     Connection listener for network based connections.
+    ///     Abstract base class for a <see cref="ConnectionListener"/> for network based connections.
     /// </summary>
     public abstract class NetworkConnectionListener : ConnectionListener
     {
         /// <summary>
-        ///     The IP address we're listening on.
+        ///     The local IP address the listener is listening for new clients on.
         /// </summary>
         public IPAddress IPAddress { get; protected set; }
 
         /// <summary>
-        ///     The port we're listening on.
+        ///     The port the listener is listening for new clients on.
         /// </summary>
         public int Port { get; protected set; }
     }
index 66947d7a49dd8865d8e0d22184a2aaac6bbc7466..f8b10d3890b94d992a2f9767da3282ed5f115fb9 100644 (file)
@@ -11,28 +11,41 @@ namespace Hazel
     /// <summary>
     ///     Represents an endpoint to a remote resource on a network.
     /// </summary>
-    public class NetworkEndPoint : ConnectionEndPoint
+    /// <remarks>
+    ///     This wraps a <see cref="System.Net.EndPoint"/> for connecting across a network using protocols like TCP or UDP.
+    /// </remarks>
+    public sealed class NetworkEndPoint : ConnectionEndPoint
     {
         /// <summary>
-        ///     The EndPoint this points to.
+        ///     The <see cref="System.Net.EndPoint">EndPoint</see> this points to.
         /// </summary>
         public EndPoint EndPoint { get; set; }
 
         /// <summary>
-        ///     Creates a NetworkEndPoint from a given EndPoint.
+        ///     The <see cref="IPMode"/> this will instruct connections to use.
         /// </summary>
-        /// <param name="endPoint">The endpoint we represent./param>
-        public NetworkEndPoint(EndPoint endPoint)
+        public IPMode IPMode { get; set; }
+
+        /// <summary>
+        ///     Creates a NetworkEndPoint from a given <see cref="System.Net.EndPoint">EndPoint</see>.
+        /// </summary>
+        /// <param name="endPoint">The end point to wrap./param>
+        public NetworkEndPoint(EndPoint endPoint, IPMode mode = IPMode.IPv4AndIPv6)
         {
             this.EndPoint = endPoint;
+            this.IPMode = mode;
         }
 
         /// <summary>
-        ///     Create a NetworkEndPoint to the specified address and port.
+        ///     Create a NetworkEndPoint to the specified <see cref="System.Net.IPAddress">IPAddress</see> 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))
+        /// <remarks>
+        ///     When using this constructor <see cref="EndPoint"/> will contain an <see cref="IPEndPoint"/>.
+        /// </remarks>
+        public NetworkEndPoint(IPAddress address, int port, IPMode mode = IPMode.IPv4AndIPv6)
+            : this(new IPEndPoint(address, port))
         {
 
         }
@@ -42,7 +55,11 @@ namespace Hazel
         /// </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)
+        /// <remarks>
+        ///     When using this constructor <see cref="EndPoint"/> will contain an <see cref="IPEndPoint"/>.
+        /// </remarks>
+        public NetworkEndPoint(string IP, int port, IPMode mode = IPMode.IPv4AndIPv6)
+            : this(IPAddress.Parse(IP), port)
         {
 
         }
index 6cdf3aa5d9a7803524899d89c2a94b9ef0ba47ce..2a01dcd08148980aad11325a2fb2f506013f3187 100644 (file)
@@ -6,8 +6,15 @@ using System.Text;
 namespace Hazel
 {
     /// <summary>
-    ///     Event args for new connection events.
+    ///     Event arguments for the <see cref="ConnectionListener.NewConnection"/> event.
     /// </summary>
+    /// <remarks>
+    ///     <para>
+    ///         This contains the new connection for the client that connection and is passed to subscribers of the
+    ///         <see cref="ConnectionListener.NewConnection"/> event.
+    ///     </para>
+    ///     <include file="DocInclude/common.xml" path="docs/item[@name='Recyclable']/*" />
+    /// </remarks>
     public class NewConnectionEventArgs : EventArgs, IRecyclable
     {
         /// <summary>
@@ -18,19 +25,19 @@ namespace Hazel
         /// <summary>
         ///     Returns an instance of this object from the pool.
         /// </summary>
-        /// <returns></returns>
+        /// <returns>A new or recycled NewConnectionEventArgs object.</returns>
         internal static NewConnectionEventArgs GetObject()
         {
             return objectPool.GetObject();
         }
 
         /// <summary>
-        ///     The new connection.
+        ///     The <see cref="Connection"/> to the new client.
         /// </summary>
         public Connection Connection { get; private set; }
 
         /// <summary>
-        ///     Private constructor for thread pool.
+        ///     Private constructor for object pool.
         /// </summary>
         NewConnectionEventArgs()
         {
@@ -40,15 +47,13 @@ namespace Hazel
         /// <summary>
         ///     Sets the members of the arguments.
         /// </summary>
-        /// <param name="Connection"></param>
+        /// <param name="Connection">The new connection</param>
         internal void Set(Connection Connection)
         {
             this.Connection = Connection;
         }
 
-        /// <summary>
-        ///     Returns this object back to the object pool.
-        /// </summary>
+        /// <inheritdoc />
         public void Recycle()
         {
             objectPool.PutObject(this);
index 4a05e39015bebfd7adb8df36d4dd608b2c34fcf3..a840486c2aa1beba09308fd50477ba7a58a3ebd1 100644 (file)
@@ -11,7 +11,7 @@ namespace Hazel
     ///     A fairly simple object pool for items that will be created a lot.
     /// </summary>
     /// <typeparam name="T">The type that is pooled.</typeparam>
-    class ObjectPool<T> where T : IRecyclable
+    sealed class ObjectPool<T> where T : IRecyclable
     {
         /// <summary>
         ///     Our pool of objects
index 27251ff438bd7389ebd32bc078786a2ab9eed465..c45c08e4cd01caa8f4619a15a8badf9ae1965e38 100644 (file)
@@ -7,49 +7,53 @@ using System.Threading.Tasks;
 namespace Hazel
 {
     /// <summary>
-    ///     Specifies how a message should be sent.
+    ///     Specifies how a message should be sent between connections.
     /// </summary>
     [Flags]
     public enum SendOption : byte
     {
         /// <summary>
-        ///     Requests unreliable delivery with no framentation or ordering.
+        ///     Requests unreliable delivery with no framentation.
         /// </summary>
+        /// <remarks>
+        ///     Sending data using unreliable delivery means that data is not guaranteed to arrive at it's destination nor is 
+        ///     it guarenteed to arrive only once. However, unreliable delivery can be faster than other methods and it 
+        ///     typically requires a smaller number of protocol bytes than other methods. There is also typically less 
+        ///     processing involved and less memory needed as packets are not stored once sent.
+        /// </remarks>
         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.
+        ///     Requests data be sent reliably but with no fragmentation.
         /// </summary>
         /// <remarks>
-        ///     Any packets that are out of order in this option will be dropped.
+        ///     Sending data reliably means that data is guarenteed to arrive and to arrive only once. Reliable delivery
+        ///     typically requires more processing, more memory (as packets need to be stored in case they need resending), 
+        ///     a larger number of protocol bytes and can be slower than unreliable delivery.
         /// </remarks>
-        Ordered = 2,
+        Reliable = 16,
 
         /// <summary>
-        ///     Requests that data should be sent in order and reliably.
+        ///     Requests data be sent so that large messages are fragmented into smaller chunks of
+        ///     data and reassembled when received.
         /// </summary>
         /// <remarks>
-        ///     Only messages that are sent using OrderedReliable or OrderedFragmentedReliable will arrive
-        ///     in order, other messages
-        ///     may arrive in between.
+        ///     Fragmented messages allow large amounts of data to be transmitted in smaller chunks when using connections 
+        ///     that do not support the transmission of large messages. Without specifying reliable delivery there is no 
+        ///     guarentee that the message will arrive but any incomplete messages will be simply be discarded.
         /// </remarks>
-        OrderedReliable = 3,
+        Fragmented = 32,
 
         /// <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
+        /// <remarks>
+        ///     Fragmented messages allow large amounts of data to be transmitted in smaller chunks when using connections 
+        ///     that do not support the transmission of large messages. By specifying reliable delivery messages are 
+        ///     guaranteed to arrive and to arrive only once but the sending process may require more memory, processing,
+        ///     a larger number protocol bytes and may be slower than sending unreliably.
+        /// </remarks>
+        FragmentedReliable = 48
     }
 }
index 16624f1ab439257e9024a44618487dd054604a2e..212467dee879f5da1aff10ca2bdc8f32aa720929 100644 (file)
@@ -7,23 +7,23 @@ using System.Threading.Tasks;
 namespace Hazel
 {
     /// <summary>
-    ///     Extra internal states for SendOption enumeration.
+    ///     Extra internal states for SendOption enumeration when using UDP.
     /// </summary>
     enum SendOptionInternal : byte
     {
         /// <summary>
         ///     Hello message for initiating communication.
         /// </summary>
-        Hello = 253,
+        Hello = 128,
 
         /// <summary>
         ///     Message for discontinuing communication.
         /// </summary>
-        Disconnect = 254,
+        Disconnect = 129,
 
         /// <summary>
         ///     Message acknowledging the receipt of a message.
         /// </summary>
-        Acknowledgement = 255
+        Acknowledgement = 130
     }
 }
index 846003c7d16f559582872f74c695f08f7a8c0a82..b025b5a750619c9b8f38922c8362b123f02027a4 100644 (file)
@@ -9,7 +9,7 @@ namespace Hazel
     /// <summary>
     ///     Represents the state of the current receive operation for TCP connections.
     /// </summary>
-    public struct StateObject
+    struct StateObject
     {
         /// <summary>
         ///     The buffer we're receiving.
index f2462f16b9313e618ae9c648e7640e3a6bb01552..39905d516e75140a4152c5be45a6bc6e159cca71 100644 (file)
@@ -5,43 +5,41 @@ 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 : NetworkConnection
+    /// <inheritdoc />
+    public sealed class TcpConnection : NetworkConnection
     {
         /// <summary>
         ///     The socket we're managing.
         /// </summary>
-        public Socket Socket { get; private set; }
+        Socket socket;
+
+        /// <summary>
+        ///     Lock for the socket.
+        /// </summary>
+        Object socketLock = new Object();
 
         /// <summary>
         ///     Creates a TcpConnection from a given TCP Socket.
         /// </summary>
-        /// <param name="socket"></param>
+        /// <param name="socket">The TCP socket to wrap.</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)
+            lock (this.socketLock)
             {
-                this.Socket.NoDelay = true;
+                this.EndPoint = new NetworkEndPoint(socket.RemoteEndPoint);
+                this.RemoteEndPoint = socket.RemoteEndPoint;
+
+                this.socket = socket;
+                this.socket.NoDelay = true;
 
                 State = ConnectionState.Connected;
             }
@@ -52,10 +50,7 @@ namespace Hazel
         /// </summary>
         public TcpConnection()
         {
-            //Create and connect a socket
-            Socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, System.Net.Sockets.ProtocolType.Tcp);
-
-            Socket.NoDelay = true;
+            
         }
 
         /// <summary>
@@ -74,10 +69,8 @@ namespace Hazel
             }
         }
 
-        /// <summary>
-        ///     Connects this TCP connection to the endpoint.
-        /// </summary>
-        /// <param name="remotEndPoint">The location of the server to connect to.</param>
+        /// <inheritdoc />
+        /// <param name="remoteEndPoint">A <see cref="NetworkEndPoint"/> to connect to.</param>
         public override void Connect(ConnectionEndPoint remoteEndPoint)
         {
             NetworkEndPoint nep = remoteEndPoint as NetworkEndPoint;
@@ -85,65 +78,74 @@ namespace Hazel
             {
                 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)
+            lock (socketLock)
             {
                 if (State != ConnectionState.NotConnected)
                     throw new InvalidOperationException("Cannot connect as the Connection is already connected.");
 
+                this.EndPoint = remoteEndPoint;
+                this.RemoteEndPoint = nep.EndPoint;
+
+                //Create a socket
+                if (nep.IPMode == IPMode.IPv4)
+                    socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, System.Net.Sockets.ProtocolType.Tcp);
+                else
+                {
+                    if (!Socket.OSSupportsIPv6)
+                        throw new HazelException("IPV6 not supported!");
+
+                    socket = new Socket(AddressFamily.InterNetworkV6, SocketType.Stream, System.Net.Sockets.ProtocolType.Tcp);
+                }
+
+                //Set parameters of socket
+                if (nep.IPMode == IPMode.IPv4AndIPv6)
+                    socket.DualMode = true;
+
+                socket.NoDelay = true;
+
+                //Connect
                 State = ConnectionState.Connecting;
 
                 try
                 {
-                    Socket.Connect(nep.EndPoint);
+                    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);
-            }
+                //Start receiving data
+                StartListening();
 
-            //Set connected
-            lock (Socket)
+                //Set connected
                 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>
+        /// <inheritdoc/>
         /// <remarks>
-        ///     The sendOptions parameter is ignored by the TcpConnection as TCP only supports OrderedFragmentedReliable communication.
+        ///     <include file="DocInclude/common.xml" path="docs/item[@name='Connection_SendBytes_General']/*" />
+        ///     <para>
+        ///         The sendOption parameter is ignored by the TcpConnection as TCP only supports FragmentedReliable 
+        ///         communication, specifying anything else will have no effect.
+        ///     </para>
         /// </remarks>
-        public override void WriteBytes(byte[] bytes, SendOption sendOption = SendOption.OrderedFragmentedReliable)
+        public override void SendBytes(byte[] bytes, SendOption sendOption = SendOption.FragmentedReliable)
         {
             //Get bytes for length
-            byte[] fullBytes = Utility.AppendLengthHeader(bytes);
+            byte[] fullBytes = AppendLengthHeader(bytes);
 
             //Write the bytes to the socket
-            lock (Socket)
+            lock (socketLock)
             {
                 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);
+                    socket.BeginSend(fullBytes, 0, fullBytes.Length, SocketFlags.None, null, null);
                 }
                 catch (SocketException e)
                 {
@@ -159,11 +161,11 @@ namespace Hazel
         /// <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)
+        /// <param name="bytes">The 4 header bytes read.</param>
+        void HeaderReadCallback(byte[] bytes)
         {
             //Get length 
-            int length = Utility.GetLengthFromBytes(bytes);
+            int length = GetLengthFromBytes(bytes);
 
             //Begin receiving the body
             try
@@ -179,8 +181,8 @@ namespace Hazel
         /// <summary>
         ///     Callback for when a body has been read.
         /// </summary>
-        /// <param name="result"></param>
-        protected virtual void BodyReadCallback(byte[] bytes)
+        /// <param name="bytes">The data bytes received by the connection.</param>
+        void BodyReadCallback(byte[] bytes)
         {
             //Begin receiving from the start
             StartWaitingForHeader();
@@ -188,13 +190,13 @@ namespace Hazel
             Statistics.LogReceive(bytes.Length, bytes.Length + 4);
 
             //Fire DataReceived event
-            InvokeDataReceived(bytes, SendOption.OrderedFragmentedReliable);
+            InvokeDataReceived(bytes, SendOption.FragmentedReliable);
         }
 
         /// <summary>
         ///     Starts this connections waiting for the header.
         /// </summary>
-        protected void StartWaitingForHeader()
+        void StartWaitingForHeader()
         {
             StartWaitingForBytes(4, HeaderReadCallback);
         }
@@ -204,7 +206,7 @@ namespace Hazel
         /// </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)
+        void StartWaitingForBytes(int length, Action<byte[]> callback)
         {
             StateObject state = new StateObject(length, callback);
 
@@ -215,13 +217,13 @@ namespace Hazel
         ///     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)
+        void StartWaitingForChunk(StateObject state)
         {
-            lock (Socket)
+            lock (socketLock)
             {
                 //Double check we've not disconnected then begin receiving
                 if (State == ConnectionState.Connected || State == ConnectionState.Connecting)
-                    Socket.BeginReceive(state.buffer, state.totalBytesReceived, state.buffer.Length, SocketFlags.None, ChunkReadCallback, state);
+                    socket.BeginReceive(state.buffer, state.totalBytesReceived, state.buffer.Length, SocketFlags.None, ChunkReadCallback, state);
                 else
                     HandleDisconnect();
             }
@@ -231,15 +233,15 @@ namespace Hazel
         ///     Called when a chunk has been read.
         /// </summary>
         /// <param name="result"></param>
-        protected virtual void ChunkReadCallback(IAsyncResult result)
+        void ChunkReadCallback(IAsyncResult result)
         {
             int bytesReceived;
 
             //End the receive operation
             try
             {
-                lock (Socket)
-                    bytesReceived = Socket.EndReceive(result);
+                lock (socketLock)
+                    bytesReceived = socket.EndReceive(result);
             }
             catch (ObjectDisposedException)
             {
@@ -283,7 +285,7 @@ namespace Hazel
         {
             bool invoke = false;
 
-            lock (Socket)
+            lock (socketLock)
             {
                 //Only invoke the disconnected event if we're not already disconnecting
                 if (State == ConnectionState.Connected)
@@ -303,19 +305,51 @@ namespace Hazel
         }
 
         /// <summary>
-        ///     Closes this connections safely.
+        ///     Appends the length header to the bytes.
         /// </summary>
+        /// <param name="bytes">The source bytes.</param>
+        /// <returns>The new bytes.</returns>
+        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">The bytes received.</param>
+        /// <returns>The number of bytes.</returns>
+        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];
+        }
+
+        /// <inheritdoc />
         protected override void Dispose(bool disposing)
         {
             if (disposing)
             {
-                lock (Socket)
+                lock (socketLock)
                 {
                     State = ConnectionState.NotConnected;
 
-                    if (Socket.Connected)
-                        Socket.Shutdown(SocketShutdown.Send);
-                    Socket.Dispose();
+                    if (socket.Connected)
+                        socket.Shutdown(SocketShutdown.Send);
+                    socket.Dispose();
                 }
             }
 
index 29e9d08215a3b45b51d2e46d6322fd0436886b5f..2eaa8b43a2c03cb382084c07fe5ec6ee8643b43c 100644 (file)
@@ -6,19 +6,15 @@ 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
-*/
+//TODO replace copyright notices with MIT licenses
 
 namespace Hazel
 {
     /// <summary>
     ///     Listens for new TCP connections and creates TCPConnections for them.
     /// </summary>
-    public class TcpConnectionListener : NetworkConnectionListener
+    /// <inheritdoc />
+    public sealed class TcpConnectionListener : NetworkConnectionListener
     {
         /// <summary>
         ///     The socket listening for connections.
@@ -26,21 +22,31 @@ namespace Hazel
         Socket listener;
 
         /// <summary>
-        ///     Creates a new ConnectionListener for the given IP and port.
+        ///     Creates a new TcpConnectionListener for the given <see cref="IPAddress"/>, port and <see cref="IPMode"/>.
         /// </summary>
-        /// <param name="ipAdress">The IPAddress to listen on.</param>
+        /// <param name="IPAddress">The IPAddress to listen on.</param>
         /// <param name="port">The port to listen on.</param>
-        public TcpConnectionListener(IPAddress IPAddress, int port)
+        /// <param name="mode">The <see cref="IPMode"/> to listen with.</param>
+        public TcpConnectionListener(IPAddress IPAddress, int port, IPMode mode = IPMode.IPv4AndIPv6)
         {
             this.IPAddress = IPAddress;
             this.Port = port;
 
-            this.listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
+            if (mode == IPMode.IPv4)
+                this.listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
+            else
+            {
+                if (!Socket.OSSupportsIPv6)
+                    throw new HazelException("IPV6 not supported!");
+
+                this.listener = new Socket(AddressFamily.InterNetworkV6, SocketType.Stream, ProtocolType.Tcp);
+            }
+            
+            if (mode == IPMode.IPv4AndIPv6)
+                this.listener.DualMode = true;
         }
 
-        /// <summary>
-        ///     Makes this connection listener begin listening for connections.
-        /// </summary>
+        /// <inheritdoc />
         public override void Start()
         {
             try
@@ -92,10 +98,7 @@ namespace Hazel
             }
         }
 
-        /// <summary>
-        ///     Called when the object is being disposed.
-        /// </summary>
-        /// <param name="disposing">Are we being disposed?</param>
+        /// <inheritdoc/>
         protected override void Dispose(bool disposing)
         {
             if (disposing)
index 08cee338f7b7e84985ed971aa694359512d0fa1f..b867fd1bf821fcb9b7b5a9f2739ae45e1b044128 100644 (file)
@@ -9,13 +9,22 @@ using System.Threading.Tasks;
 
 namespace Hazel
 {
-    public class UdpClientConnection : UdpConnection
+    /// <summary>
+    ///     Represents a client's connection to a server that uses the UDP protocol.
+    /// </summary>
+    /// <inheritdoc/>
+    public sealed class UdpClientConnection : UdpConnection
     {
         /// <summary>
         ///     The socket we're connected via.
         /// </summary>
         Socket socket;
 
+        /// <summary>
+        ///     The lock for the socket.
+        /// </summary>
+        Object socketLock = new Object();
+
         /// <summary>
         ///     The buffer to store incomming data in.
         /// </summary>
@@ -27,27 +36,10 @@ namespace Hazel
         public UdpClientConnection()
             : base()
         {
-            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)
-        {
-            if (State != ConnectionState.Connected)
-                throw new InvalidOperationException("Could not send data as this Connection is not connected. Did you disconnect?");
-
-            //Add header information and send
-            HandleSend(bytes, (byte)sendOption);
+            
         }
 
-        /// <summary>
-        ///     Writes bytes to the socket.
-        /// </summary>
-        /// <param name="bytes">The bytes to send.</param>
+        /// <inheritdoc />
         protected override void WriteBytesToConnection(byte[] bytes)
         {
             //Pack
@@ -55,7 +47,7 @@ namespace Hazel
             args.SetBuffer(bytes, 0, bytes.Length);
             args.RemoteEndPoint = RemoteEndPoint;
 
-            lock (socket)
+            lock (socketLock)
             {
                 if (State != ConnectionState.Connected && State != ConnectionState.Connecting)
                     throw new InvalidOperationException("Could not send data as this Connection is not connected and is not connecting. Did you disconnect?");
@@ -78,9 +70,8 @@ namespace Hazel
             }
         }
 
-        /// <summary>
-        ///     Connects this Connection to a given remote server and begins listening for data.
-        /// </summary>
+        /// <inheritdoc />
+        /// <param name="remoteEndPoint">A <see cref="NetworkEndPoint"/> to connect to.</param>
         public override void Connect(ConnectionEndPoint remoteEndPoint)
         {
             NetworkEndPoint nep = remoteEndPoint as NetworkEndPoint;
@@ -89,11 +80,19 @@ namespace Hazel
                 throw new ArgumentException("The remote end point of a UDP connection must be a NetworkEndPoint.");
             }
 
-            this.EndPoint = nep;
-            this.RemoteEndPoint = nep.EndPoint;
-
-            lock (socket)
+            lock (socketLock)
             {
+                this.EndPoint = nep;
+                this.RemoteEndPoint = nep.EndPoint;
+
+                if (nep.IPMode == IPMode.IPv4)
+                    socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
+                else
+                {
+                    socket = new Socket(AddressFamily.InterNetworkV6, SocketType.Dgram, ProtocolType.Udp);
+                    socket.DualMode = true;
+                }
+
                 if (State != ConnectionState.NotConnected)
                     throw new InvalidOperationException("Cannot connect as the Connection is already connected.");
 
@@ -137,7 +136,7 @@ namespace Hazel
 
             //Write bytes to the server to tell it hi (and to punch a hole in our NAT, if present)
             //When acknowledged set the state to connected
-            SendHello(() => State = ConnectionState.Connected);
+            SendHello(() => { lock (socketLock) State = ConnectionState.Connected; });
 
             //Wait till hello packet is acknowledged and the state is set to Connected
             WaitOnConnect();
@@ -148,7 +147,8 @@ namespace Hazel
         /// </summary>
         void StartListeningForData()
         {
-            socket.BeginReceive(dataBuffer, 0, dataBuffer.Length, SocketFlags.None, ReadCallback, dataBuffer);
+            lock (socketLock)
+                socket.BeginReceive(dataBuffer, 0, dataBuffer.Length, SocketFlags.None, ReadCallback, dataBuffer);
         }
 
         /// <summary>
@@ -162,7 +162,7 @@ namespace Hazel
             //End the receive operation
             try
             {
-                lock (socket)
+                lock (socketLock)
                     bytesReceived = socket.EndReceive(result);
             }
             catch (ObjectDisposedException)
@@ -192,8 +192,7 @@ namespace Hazel
             //Begin receiving again
             try
             {
-                lock (socket)
-                    StartListeningForData();
+                StartListeningForData();
             }
             catch (SocketException e)
             {
@@ -209,15 +208,12 @@ namespace Hazel
                 InvokeDataReceived(buffer, sendOption);
         }
 
-        /// <summary>
-        ///     Called when the socket has been disconnected at the remote host.
-        /// </summary>
-        /// <param name="e">The exception if one was the cause.</param>
+        /// <inheritdoc />
         protected override void HandleDisconnect(HazelException e = null)
         {
             bool invoke = false;
 
-            lock (socket)
+            lock (socketLock)
             {
                 //Only invoke the disconnected event if we're not already disconnecting
                 if (State == ConnectionState.Connected)
@@ -236,15 +232,13 @@ namespace Hazel
             }
         }
 
-        /// <summary>
-        ///     Safely closes this connection.
-        /// </summary>
+        /// <inheritdoc />
         protected override void Dispose(bool disposing)
         {
             //Dispose of the socket
             if (disposing)
             {
-                lock (socket)
+                lock (socketLock)
                 {
                     State = ConnectionState.NotConnected;
 
index ee1b90a698e9be3c484719ef4e1051963ae21fd0..e9ab1220f4af61e1934ca270188dc3c5c18092f9 100644 (file)
@@ -8,16 +8,20 @@ using System.Threading.Tasks;
 
 namespace Hazel
 {
-    /// <summary>
-    ///     UdpConnection part which handles keepalive packets.
-    /// </summary>
     partial class UdpConnection
     {
         /// <summary>
-        ///     The interval from data being received or transmitted to a keepalive packet being sent.
+        ///     The interval from data being received or transmitted to a keepalive packet being sent in milliseconds.
         /// </summary>
         /// <remarks>
-        ///     Set to System.Threading.Timeout.Infinite to disable keepalive packets.
+        ///     <para>
+        ///         Keepalive packets serve to close connections when an endpoint abruptly disconnects and to ensure than any
+        ///         NAT devices do not close their translation for our argument. By ensuring there is regular contact the
+        ///         connection can detect and prevent these issues.
+        ///     </para>
+        ///     <para>
+        ///         The default value is 10 seconds, set to System.Threading.Timeout.Infinite to disable keepalive packets.
+        ///     </para>
         /// </remarks>
         public int KeepAliveInterval
         {
index 80038c6092514710ca8028612631f53150b3d36f..146919396064bfccb86ee1ae351c4ad4b0b2d344 100644 (file)
@@ -10,13 +10,14 @@ namespace Hazel
 {
     partial class UdpConnection
     {
-        //TODO recycle dataevents and things?
-        
         /// <summary>
         ///     The starting timeout, in miliseconds, at which data will be resent.
         /// </summary>
         /// <remarks>
-        ///     On each resend this is doubled for that packet.
+        ///     For reliable delivery data is resent at specified intervals unless an acknowledgement is received from the 
+        ///     receiving device. The ResendTimeout specifies the interval between the packets being resent, each time a packet
+        ///     is resent the interval is doubled for that packet until the number of resends exceeds the 
+        ///     <see cref="ResendsBeforeDisconnect"/> value.
         /// </remarks>
         public int ResendTimeout { get { return resendTimeout; } set { resendTimeout = value; } }
         private volatile int resendTimeout = 200;        //TODO this based of average ping?
@@ -47,10 +48,16 @@ namespace Hazel
         volatile bool hasReceivedSomething = false;
 
         /// <summary>
-        ///     The maximum times a message should be retransmitted before marking the endpoint as disconnected.
+        ///     The maximum times a message should be resent before marking the endpoint as disconnected.
         /// </summary>
-        public int RetransmissionsBeforeDisconnect { get { return retransmissionsBeforeDisconnect; } set { retransmissionsBeforeDisconnect = value; } }
-        private volatile int retransmissionsBeforeDisconnect = 3;
+        /// <remarks>
+        ///     Reliable packets will be resent at an interval defined in <see cref="ResendInterval"/> for the number of times
+        ///     specified here. Once a packet has been retransmitted this number of times and has not been acknowledged the
+        ///     connection will be marked as disconnected and the <see cref="Connection.Disconnected">Disconnected</see> event
+        ///     will be invoked.
+        /// </remarks>
+        public int ResendsBeforeDisconnect { get { return resendsBeforeDisconnect; } set { resendsBeforeDisconnect = value; } }
+        private volatile int resendsBeforeDisconnect = 3;
 
         /// <summary>
         ///     Class to hold packet data
@@ -155,22 +162,23 @@ namespace Hazel
                     bytes,
                     (Packet p) =>
                     {
-                        WriteBytesToConnection(p.Data);
-
                         //Double packet timeout
                         lock (p.Timer)
                         {
                             if (!p.Acknowledged)
                             {
-                                p.Timer.Change(p.LastTimeout *= 2, Timeout.Infinite);       //TODO disconnect after x tries
-                                if (++p.Retransmissions >= RetransmissionsBeforeDisconnect)
+                                p.Timer.Change(p.LastTimeout *= 2, Timeout.Infinite);
+                                if (++p.Retransmissions > ResendsBeforeDisconnect)
                                 {
                                     HandleDisconnect();
                                     p.Recycle();
+                                    return;
                                 }
                             }
                         }
 
+                        WriteBytesToConnection(p.Data);
+
                         Trace.WriteLine("Resend.");
                     },
                     resendTimeout,
@@ -257,6 +265,11 @@ namespace Hazel
             }
         }
 
+        /// <summary>
+        ///     Sends an acknowledgement for a packet given its identification bytes.
+        /// </summary>
+        /// <param name="byte1">The first identification byte.</param>
+        /// <param name="byte2">The second identification byte.</param>
         internal void SendAck(byte byte1, byte byte2)
         {
             //Always reply with acknowledgement in order to stop the sender repeatedly sending it
index f5ac140896fc8d63f2cd1b184bc12e9f7218592c..3d5b6c22bcd20005e47b0800a766651d5e1448c9 100644 (file)
@@ -6,36 +6,53 @@ 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>
+    /// <inheritdoc />
     public abstract partial class UdpConnection : NetworkConnection
     {
+        /// <summary>
+        ///     Creates a new UdpConnection and initializes the keep alive timer.
+        /// </summary>
+        protected UdpConnection()
+        {
+            InitializeKeepAliveTimer();
+        }
+
         /// <summary>
         ///     Writes the given bytes to the connection.
         /// </summary>
         /// <param name="bytes">The bytes to write.</param>
         protected abstract void WriteBytesToConnection(byte[] bytes);
 
-        protected UdpConnection()
+        /// <inheritdoc/>
+        /// <remarks>
+        ///     <include file="DocInclude/common.xml" path="docs/item[@name='Connection_SendBytes_General']/*" />
+        ///     <para>
+        ///         Udp connections can currently send messages using <see cref="SendOption.None"/> and
+        ///         <see cref="SendOption.Reliable"/>. Fragmented messages are not currently supported and will default to
+        ///         <see cref="SendOption.None"/> until implemented.
+        ///     </para>
+        /// </remarks>
+        public override void SendBytes(byte[] bytes, SendOption sendOption = SendOption.None)
         {
-            InitializeKeepAliveTimer();
+            //Early check
+            if (State != ConnectionState.Connected)
+                throw new InvalidOperationException("Could not send data as this Connection is not connected. Did you disconnect?");
+
+            //Add header information and send
+            HandleSend(bytes, (byte)sendOption);
         }
 
         /// <summary>
-        ///     Handles the reliable/fragmented/ordered sending from this connection.
+        ///     Handles the reliable/fragmented sending from this connection.
         /// </summary>
         /// <param name="data">The data being sent.</param>
-        /// <param name="sendOption">The send option as a byte.</param>
+        /// <param name="sendOption">The <see cref="SendOption"/> specified as its byte value.</param>
+        /// <param name="ackCallback">The callback to invoke when this packet is acknowledged.</param>
         /// <returns>The bytes that should actually be sent.</returns>
         protected void HandleSend(byte[] data, byte sendOption, Action ackCallback = null)
         {
@@ -77,7 +94,7 @@ namespace Hazel
         /// <summary>
         ///     Handles the receiving of data.
         /// </summary>
-        /// <param name="buffer">The array of the data received.</param>
+        /// <param name="buffer">The buffer containing the bytes received.</param>
         /// <param name="bytesReceived">The number of bytes that were received.</param>
         /// <returns>The bytes of data received.</returns>
         protected byte[] HandleReceive(byte[] buffer, int bytesReceived)
@@ -132,9 +149,7 @@ namespace Hazel
             HandleSend(new byte[0], (byte)SendOptionInternal.Hello, acknowledgeCallback);
         }
 
-        /// <summary>
-        ///     Closes this connection safely.
-        /// </summary>
+        /// <inheritdoc/>
         public override void Close()
         {
             HandleSend(new byte[0], (byte)SendOptionInternal.Disconnect);       //TODO Should disconnect wait for an ack?
@@ -148,10 +163,7 @@ namespace Hazel
         /// <param name="e">The exception if one was the cause.</param>
         protected abstract void HandleDisconnect(HazelException e = null);
 
-        /// <summary>
-        ///     Called when things are being disposed of
-        /// </summary>
-        /// <param name="disposing"></param>
+        /// <inheritdoc/>
         protected override void Dispose(bool disposing)
         {
             if (disposing)
index 65d8484397db3e673d8d567d0fcc7f161738915d..284a5fc4a9735fccd2dc2a7d8e4976afd0d67c93 100644 (file)
@@ -6,18 +6,12 @@ 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 UDP connections and creates UdpConnections for them.
     /// </summary>
+    /// <inheritdoc />
     public class UdpConnectionListener : NetworkConnectionListener
     {
         /// <summary>
@@ -31,21 +25,26 @@ namespace Hazel
         Dictionary<EndPoint, UdpServerConnection> connections = new Dictionary<EndPoint, UdpServerConnection>();
 
         /// <summary>
-        ///     Creates a new ConnectionListener for the given IP and port.
+        ///     Creates a new ConnectionListener for the given <see cref="IPAddress"/>, port and <see cref="IPMode"/>.
         /// </summary>
-        /// <param name="ipAdress">The IPAddress to listen on.</param>
+        /// <param name="IPAddress">The IPAddress to listen on.</param>
         /// <param name="port">The port to listen on.</param>
-        public UdpConnectionListener(IPAddress IPAddress, int port)
+        /// <param name="mode">The <see cref="IPMode"/> to listen with.</param>
+        public UdpConnectionListener(IPAddress IPAddress, int port, IPMode mode = IPMode.IPv4AndIPv6)
         {
             this.IPAddress = IPAddress;
             this.Port = port;
 
-            this.listener = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
+            if (mode == IPMode.IPv4)
+                this.listener = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
+            else
+            {
+                this.listener = new Socket(AddressFamily.InterNetworkV6, SocketType.Dgram, ProtocolType.Udp);
+                this.listener.DualMode = true;
+            }
         }
 
-        /// <summary>
-        ///     Instruct the listener to begin listening for connections.
-        /// </summary>
+        /// <inheritdoc />
         public override void Start()
         {
             try
@@ -186,10 +185,7 @@ namespace Hazel
                 connections.Remove(endPoint);
         }
 
-        /// <summary>
-        ///     Called when the listener is being disposed of
-        /// </summary>
-        /// <param name="disposing"></param>
+        /// <inheritdoc />
         protected override void Dispose(bool disposing)
         {
             if (disposing)
index f1008a48f965b1501a6f2e4e2004b7acba468c4f..4bbf922a0dac65a53ec4e5e05ae81e75a1acf2da 100644 (file)
@@ -5,20 +5,21 @@ 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>
+    ///     Represents a servers's connection to a client that uses the UDP protocol.
+    /// </summary>
+    /// <inheritdoc/>
+    sealed class UdpServerConnection : UdpConnection
     {
         /// <summary>
         ///     The connection listener that we use the socket of.
         /// </summary>
+        /// <remarks>
+        ///     Udp server connections utilize the same socket in the listener for sends/receives, this is the listener that 
+        ///     created this connection and is hence the listener this conenction sends and receives via.
+        /// </remarks>
         public UdpConnectionListener Listener { get; private set; }
 
         /// <summary>
@@ -29,7 +30,8 @@ namespace Hazel
         /// <summary>
         ///     Creates a UdpConnection for the virtual connection to the endpoint.
         /// </summary>
-        /// <param name="socket"></param>
+        /// <param name="listener">The listener that created this connection.</param>
+        /// <param name="endPoint">The endpoint that we are connected to.</param>
         internal UdpServerConnection(UdpConnectionListener listener, EndPoint endPoint)
             : base()
         {
@@ -40,20 +42,7 @@ namespace Hazel
             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)
-        {
-            HandleSend(bytes, (byte)sendOption);
-        }
-
-        /// <summary>
-        ///     Writes bytes to the listener to send.
-        /// </summary>
-        /// <param name="bytes">bytes to send.</param>
+        /// <inheritdoc />
         protected override void WriteBytesToConnection(byte[] bytes)
         {
             lock (stateLock)
@@ -65,9 +54,7 @@ namespace Hazel
             }
         }
 
-        /// <summary>
-        ///     Connects this Connection to a given remote server.
-        /// </summary>
+        /// <inheritdoc />
         /// <remarks>
         ///     This will always throw an InvalidOperationException.
         /// </remarks>
@@ -88,10 +75,7 @@ namespace Hazel
                 InvokeDataReceived(data, (SendOption)buffer[0]);
         }
 
-        /// <summary>
-        ///     Called when the socket has been disconnected at the remote host.
-        /// </summary>
-        /// <param name="e">The exception if one was the cause.</param>
+        /// <inheritdoc />
         protected override void HandleDisconnect(HazelException e = null)
         {
             bool invoke = false;
@@ -115,9 +99,7 @@ namespace Hazel
             }
         }
 
-        /// <summary>
-        ///     Safely closes this connection.
-        /// </summary>
+        /// <inheritdoc />
         protected override void Dispose(bool disposing)
         {
             //Here we just need to inform the listener we no longer need data.
diff --git a/Hazel/Utility.cs b/Hazel/Utility.cs
deleted file mode 100644 (file)
index 98357cc..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-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];
-        }
-    }
-}