From 544fedd8d63b081330232294538b3120cc300561 Mon Sep 17 00:00:00 2001 From: JamJar00 Date: Sun, 29 May 2016 15:03:33 +0100 Subject: [PATCH] Added documentation and IPv6 support --- Hazel/Connection.cs | 145 ++++++++++++++++---- Hazel/ConnectionEndPoint.cs | 4 + Hazel/ConnectionListener.cs | 55 +++++++- Hazel/ConnectionState.cs | 2 +- Hazel/ConnectionStatistics.cs | 59 +++++++- Hazel/DataEventArgs.cs | 27 ++-- Hazel/DisconnectedEventArgs.cs | 22 ++- Hazel/DocInclude/TcpClientExample.cs | 24 ++++ Hazel/DocInclude/UdpClientExample.cs | 24 ++++ Hazel/DocInclude/UdpListenerExample.cs | 23 ++++ Hazel/DocInclude/common.xml | 31 +++++ Hazel/Hazel.csproj | 13 +- Hazel/Hazel.snk | Bin 596 -> 0 bytes Hazel/IRecyclable.cs | 10 ++ Hazel/NetworkConnection.cs | 13 +- Hazel/NetworkConnectionListener.cs | 13 +- Hazel/NetworkEndPoint.cs | 33 +++-- Hazel/NewConnectionEventArgs.cs | 21 +-- Hazel/ObjectPool.cs | 2 +- Hazel/SendOption.cs | 48 ++++--- Hazel/SendOptionInternal.cs | 8 +- Hazel/StateObject.cs | 2 +- Hazel/TcpConnection.cs | 180 +++++++++++++++---------- Hazel/TcpConnectionListener.cs | 39 +++--- Hazel/UdpClientConnection.cs | 78 +++++------ Hazel/UdpConnection.KeepAlive.cs | 14 +- Hazel/UdpConnection.Reliable.cs | 33 +++-- Hazel/UdpConnection.cs | 50 ++++--- Hazel/UdpConnectionListener.cs | 32 ++--- Hazel/UdpServerConnection.cs | 48 +++---- Hazel/Utility.cs | 45 ------- 31 files changed, 722 insertions(+), 376 deletions(-) create mode 100644 Hazel/DocInclude/TcpClientExample.cs create mode 100644 Hazel/DocInclude/UdpClientExample.cs create mode 100644 Hazel/DocInclude/UdpListenerExample.cs create mode 100644 Hazel/DocInclude/common.xml delete mode 100644 Hazel/Hazel.snk delete mode 100644 Hazel/Utility.cs diff --git a/Hazel/Connection.cs b/Hazel/Connection.cs index 3418f63..1f67312 100644 --- a/Hazel/Connection.cs +++ b/Hazel/Connection.cs @@ -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 , January 2016 -*/ - namespace Hazel { /// - /// Handles the sending and receiving of messages through the channel to give connection orientated, packet based transmission. + /// Base class for all connections. /// + /// + /// + /// 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. + /// + /// + /// Any class inheriting from Connection should provide the 3 standard guarantees that Hazel provides: + /// + /// + /// Thread Safe + /// + /// + /// Connection Orientated + /// + /// + /// Packet/Message Based + /// + /// + /// + /// + /// public abstract class Connection : IDisposable { /// /// Called when a message has been received. /// + /// + /// + /// 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 alongside other information from the + /// event. + /// + /// + /// + /// + /// + /// public event EventHandler DataReceived; /// - /// Called when the end point disconnects from us or an error occurs. + /// Called when the end point disconnects or an error occurs. /// + /// + /// + /// 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 passed with the event. + /// + /// + /// + /// + /// + /// public event EventHandler Disconnected; /// - /// The end point of this Connection. + /// The remote end point of this Connection. /// + /// + /// This is the end point that this connection is connected to (i.e. the other device). This returns an abstract + /// which can then be cast to an appropriate end point depending on the + /// connection type. + /// public ConnectionEndPoint EndPoint { get; protected set; } /// /// The traffic statistics about this Connection. /// + /// + /// Contains statistics about the number of messages and bytes sent and received by this connection. + /// public ConnectionStatistics Statistics { get; protected set; } /// /// The state of this connection. /// + /// + /// + /// Connections go round 4 states in their lifetime: they start as to + /// indicate they have no endpoint, calling takes them into + /// , once they have received confirmation they are connected they enter + /// and finally calling sets them to + /// and then the sequence repeats back to + /// once disconnection is complete. + /// + /// + /// Data can only be sent while in and all attempts to send data when + /// in any other state will throw an InvalidOperationException. + /// + /// + /// All implementers should be aware that when this is set to it will + /// release all threads that are blocked on . + /// + /// public ConnectionState State { get @@ -71,6 +132,10 @@ namespace Hazel /// /// Constructor that initializes the ConnecitonStatistics object. /// + /// + /// This constructor initialises with empty statistics and sets to + /// . + /// protected Connection() { Statistics = new ConnectionStatistics(); @@ -79,27 +144,40 @@ namespace Hazel } /// - /// 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 . /// /// The bytes of the message to send. - /// The options this data is requested to send with. + /// The option specifying how the message should be sent. /// - /// 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. + /// + /// + /// 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. + /// /// - public abstract void WriteBytes(byte[] bytes, SendOption sendOption = SendOption.None); + public abstract void SendBytes(byte[] bytes, SendOption sendOption = SendOption.None); /// - /// Connects the connection to a remote server and begins listening. + /// Connects the connection to a server and begins listening. /// + /// + /// Calling Connect makes the connection attempt to connect to the end point that's specified in the + /// passed. This method will block until the connection attempt completes and + /// will throw a if there is a problem connecting. + /// public abstract void Connect(ConnectionEndPoint remoteEndPoint); /// - /// Invokes the DataReceived event to alert subscribers we received data. + /// Invokes the DataReceived event. /// - /// The bytes to supply. - /// The sendOption to supply. + /// The bytes received. + /// The the message was received with. + /// + /// Invokes the 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. + /// protected void InvokeDataReceived(byte[] bytes, SendOption sendOption) { DataEventArgs args = DataEventArgs.GetObject(); @@ -112,10 +190,15 @@ namespace Hazel } /// - /// Invokes the Disconnected event to alert hooked up methods there was an error or the remote end point disconnected. + /// Invokes the Disconnected event. /// /// The exception, if any, that occured to cause this. - protected void InvokeDisconnected(Exception e) + /// + /// Invokes the 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. + /// + protected void InvokeDisconnected(Exception e = null) { DisconnectedEventArgs args = DisconnectedEventArgs.GetObject(); args.Set(e); @@ -129,6 +212,11 @@ namespace Hazel /// /// Blocks until the Connection is connected. /// + /// + /// This is a helper method for waiting until the connection is connected. It will block until the + /// property is set to allowing the main thread to + /// wait until specific data is received etc. before returning to the user's code. + /// protected void WaitOnConnect() { connectWaitLock.WaitOne(); @@ -137,6 +225,17 @@ namespace Hazel /// /// Closes this connection safely. /// + /// + /// + /// Informs the end point of the connection that we are disconnecting from them and disposes of this + /// connection. + /// + /// + /// This calls and therefore sets straight to + /// . Once you call Close you will not be able to send any more + /// data using this connection and no more data will be received. + /// + /// public virtual void Close() { Dispose(); diff --git a/Hazel/ConnectionEndPoint.cs b/Hazel/ConnectionEndPoint.cs index 20a358f..3e09537 100644 --- a/Hazel/ConnectionEndPoint.cs +++ b/Hazel/ConnectionEndPoint.cs @@ -5,6 +5,10 @@ using System.Text; namespace Hazel { + /// + /// Base class for all end points of connections. + /// + /// public abstract class ConnectionEndPoint { } diff --git a/Hazel/ConnectionListener.cs b/Hazel/ConnectionListener.cs index 60452c4..e738f1b 100644 --- a/Hazel/ConnectionListener.cs +++ b/Hazel/ConnectionListener.cs @@ -10,22 +10,69 @@ namespace Hazel /// /// Base class for all connection listeners. /// + /// + /// + /// 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 + /// State of and so should be ready for + /// comunication immediately. + /// + /// + /// Each time a client connects the event will be invoked to alert all subscribers to + /// the new connection. A disconnected event is then present on the that is passed to the + /// subscribers. + /// + /// + /// public abstract class ConnectionListener : IDisposable { /// - /// Invoked when a new TCP connection is heard. + /// Invoked when a new client connects. /// + /// + /// + /// NewConnection is invoked each time a client connects to the listener. The + /// contains the new for communication with this + /// client. + /// + /// + /// Hazel doesn't store connections so it is your responsibility to keep track of the connections to your + /// server. Note that as implements if you are not storing + /// a connection then as a bare minimum you should call here in order to + /// release the connection correctly. + /// + /// + /// + /// + /// + /// public event EventHandler NewConnection; - + //TODO add threadsafe markers on all xmldocs /// /// Makes this connection listener begin listening for connections. /// + /// + /// + /// This instructs the listener to begin listening for new clients connecting to the server. When a new client + /// connects the event will be invoked containing the connection to the new client. + /// + /// + /// To stop listening you should call . + /// + /// + /// + /// + /// public abstract void Start(); /// - /// Invokes the NewConnection event with the supplied args. + /// Invokes the NewConnection event with the supplied connection. /// - /// The arguments for the event. + /// The connection to pass to subscribers. + /// + /// Implementers should call this to invoke the event before data is received so that + /// subscribers do not miss any data that may have been sent immediately after connecting. + /// protected void InvokeNewConnection(Connection connection) { //Get new args diff --git a/Hazel/ConnectionState.cs b/Hazel/ConnectionState.cs index c8269bf..5036b90 100644 --- a/Hazel/ConnectionState.cs +++ b/Hazel/ConnectionState.cs @@ -6,7 +6,7 @@ using System.Text; namespace Hazel { /// - /// Marks the state a Connection is currently in. + /// Represents the state a is currently in. /// public enum ConnectionState { diff --git a/Hazel/ConnectionStatistics.cs b/Hazel/ConnectionStatistics.cs index 841c9d3..450c3d6 100644 --- a/Hazel/ConnectionStatistics.cs +++ b/Hazel/ConnectionStatistics.cs @@ -8,13 +8,19 @@ using System.Threading.Tasks; namespace Hazel { /// - /// Holds statistics about the traffic through a Connection. + /// Holds statistics about the traffic through a . /// + /// public class ConnectionStatistics { /// /// The number of messages sent. /// + /// + /// This is the number of messages that were sent from the , 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. + /// public long MessagesSent { get @@ -31,6 +37,16 @@ namespace Hazel /// /// The number of bytes of data sent. /// + /// + /// + /// This is the number of bytes of data (i.e. user bytes) that were sent from the , + /// 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. + /// + /// + /// For the number of bytes including protocol bytes see . + /// + /// public long DataBytesSent { get @@ -47,6 +63,17 @@ namespace Hazel /// /// The number of bytes sent in total. /// + /// + /// + /// This is the total number of bytes (the data bytes plus protocol bytes) that were sent from the + /// , 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. + /// + /// + /// For the number of data bytes excluding protocol bytes see . + /// + /// public long TotalBytesSent { get @@ -63,6 +90,10 @@ namespace Hazel /// /// The number of messages received. /// + /// + /// This is the number of messages that were received by the , incremented each time that + /// LogReceive is called by the Connection. Messages are counted before the receive event is invoked. + /// public long MessagesReceived { get @@ -79,6 +110,16 @@ namespace Hazel /// /// The number of bytes of data received. /// + /// + /// + /// This is the number of bytes of data (i.e. user bytes) that were received by the , + /// accumulated each time that LogReceive is called by the Connection. Messages are counted before the receive + /// event is invoked. + /// + /// + /// For the number of bytes including protocol bytes see . + /// + /// public long DataBytesReceived { get @@ -95,6 +136,16 @@ namespace Hazel /// /// The number of bytes received in total. /// + /// + /// + /// This is the total number of bytes (the data bytes plus protocol bytes) that were received by the + /// , accumulated each time that LogReceive is called by the Connection. Messages are + /// counted before the receive event is invoked. + /// + /// + /// For the number of data bytes excluding protocol bytes see . + /// + /// public long TotalBytesReceived { get @@ -113,6 +164,9 @@ namespace Hazel /// /// The number of bytes of data sent. /// The total number of bytes sent. + /// + /// This should be called after the data has been sent and should only be called for data that is sent sucessfully. + /// internal void LogSend(int dataLength, int totalLength) { Interlocked.Increment(ref messagesSent); @@ -125,6 +179,9 @@ namespace Hazel /// /// The number of bytes of data received. /// The total number of bytes received. + /// + /// This should be called before the received event is invoked so it is up to date for subscribers to that event. + /// internal void LogReceive(int dataLength, int totalLength) { Interlocked.Increment(ref messagesReceived); diff --git a/Hazel/DataEventArgs.cs b/Hazel/DataEventArgs.cs index a1cb059..fd56a6b 100644 --- a/Hazel/DataEventArgs.cs +++ b/Hazel/DataEventArgs.cs @@ -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 , January 2016 -*/ - namespace Hazel { + /// + /// Event arguments for the event. + /// + /// + /// + /// This contains information about messages received by a connection and is passed to subscribers of the + /// DataEvent. + /// + /// + /// public class DataEventArgs : EventArgs, IRecyclable { /// @@ -22,19 +25,19 @@ namespace Hazel /// /// Returns an instance of this object from the pool. /// - /// + /// A new or recycled DataEventArgs object. internal static DataEventArgs GetObject() { return objectPool.GetObject(); } /// - /// The bytes received. + /// The bytes received from the client. /// public byte[] Bytes { get; private set; } /// - /// The SendOption the data was sent with. + /// The the data was sent with. /// public object SendOption { get; private set; } @@ -57,9 +60,7 @@ namespace Hazel this.SendOption = sendOption; } - /// - /// Returns this object back to the object pool. - /// + /// public void Recycle() { objectPool.PutObject(this); diff --git a/Hazel/DisconnectedEventArgs.cs b/Hazel/DisconnectedEventArgs.cs index 4468eef..86d26c5 100644 --- a/Hazel/DisconnectedEventArgs.cs +++ b/Hazel/DisconnectedEventArgs.cs @@ -6,8 +6,15 @@ using System.Text; namespace Hazel { /// - /// Events args for disconnected events. + /// Event arguments for the event. /// + /// + /// + /// This contains information about the cause of a disconnection and is passed to subscribers of the + /// event. + /// + /// + /// public class DisconnectedEventArgs : IRecyclable { /// @@ -18,15 +25,20 @@ namespace Hazel /// /// Returns an instance of this object from the pool. /// - /// + /// A new or recycled DisconnectedEventArgs object. internal static DisconnectedEventArgs GetObject() { return objectPool.GetObject(); } /// - /// The exception, if any, that caused the disconnect, otherwise null. + /// The exception, if any, that caused the disconnect. /// + /// + /// If the disconnection was caused because of an exception occuring (for exemple a + /// on network based connections) this will contain the error that caused it or a + /// with the details of the exception, if the disconnection wasn't caused by an error then this will contain null. + /// public Exception Exception { get; private set; } /// @@ -46,9 +58,7 @@ namespace Hazel this.Exception = e; } - /// - /// Returns this object back to the object pool. - /// + /// public void Recycle() { objectPool.PutObject(this); diff --git a/Hazel/DocInclude/TcpClientExample.cs b/Hazel/DocInclude/TcpClientExample.cs new file mode 100644 index 0000000..cc2bdc2 --- /dev/null +++ b/Hazel/DocInclude/TcpClientExample.cs @@ -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 index 0000000..b576f26 --- /dev/null +++ b/Hazel/DocInclude/UdpClientExample.cs @@ -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 index 0000000..73c2432 --- /dev/null +++ b/Hazel/DocInclude/UdpListenerExample.cs @@ -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 index 0000000..76edc64 --- /dev/null +++ b/Hazel/DocInclude/common.xml @@ -0,0 +1,31 @@ + + + + + + As with all Hazel events it is invoked on a thread from the .NET 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. + + + + + 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 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. + + + + + This method sends a number of bytes in a message to the end point of this client using the given + 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 property for information on whether a connection is connected or not. + + + \ No newline at end of file diff --git a/Hazel/Hazel.csproj b/Hazel/Hazel.csproj index 9547f64..ce4bf63 100644 --- a/Hazel/Hazel.csproj +++ b/Hazel/Hazel.csproj @@ -36,7 +36,8 @@ true - Hazel.snk + + @@ -54,7 +55,12 @@ + + + + + @@ -76,10 +82,11 @@ - - + + Designer +