--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<Topics>
+ <Topic id="a9f1cfac-8d7d-4668-af2f-60b588018519" visible="True" isSelected="true" title="Introduction" />
+ <Topic id="4e89497f-70d3-4fb3-9c92-cfd8981ccfa9" visible="True" title="Quickstart" />
+ <Topic id="852623d7-5d68-47e2-b5db-76498da44f95" visible="True" isExpanded="true" title="Technical Details">
+ <Topic id="0ffa7468-4813-4fd0-ba65-2fa5436bc5ce" visible="True" isExpanded="true" title="Protocols">
+ <Topic id="a3d0767d-2da3-45d2-80d7-b0c05698992f" visible="True" />
+ <Topic id="490c9f89-38c8-4ccf-8af1-683ba7fb0888" visible="True" />
+ </Topic>
+ </Topic>
+</Topics>
\ No newline at end of file
--- /dev/null
+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, DataReceivedEventArgs 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();
+ }
+ }
+}
--- /dev/null
+class TcpListenerExample
+{
+ static void Main(string[] args)
+ {
+ //Setup listener
+ using (TcpConnectionListener listener = new TcpConnectionListener(new NetworkEndPoint(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();
+ }
+ }
+}
--- /dev/null
+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, DataReceivedEventArgs 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();
+ }
+ }
+}
--- /dev/null
+class UdpListenerExample
+{
+ static void Main(string[] args)
+ {
+ //Setup listener
+ using (UdpConnectionListener listener = new UdpConnectionListener(new NetworkEndPoint(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();
+ }
+ }
+}
--- /dev/null
+<?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="System.Threading.ThreadPool">ThreadPool</see> 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="Connection.State"/> property for information on whether a connection is connected or not.
+ </para>
+ </item>
+</docs>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<topic id="a9f1cfac-8d7d-4668-af2f-60b588018519" revisionNumber="1">
+ <developerConceptualDocument xmlns="http://ddue.schemas.microsoft.com/authoring/2003/5" xmlns:xlink="http://www.w3.org/1999/xlink">
+
+ <introduction>
+ <para>
+ Welcome to the Hazel documentation! Here you will find technical
+ details, API references and tutorials for getting started with Hazel.
+ </para>
+ <para>
+ Hazel Networking is an open source low level networking library for C# providing
+ connection orientated, message bassed communication via TCP, UDP and
+ RUDP. You can download it from Github
+ <externalLink>
+ <linkText>here</linkText>
+ <linkUri>https://github.com/DarkRiftNetworking/Hazel-Networking</linkUri>
+ <linkTarget>_blank</linkTarget>
+ </externalLink>
+ .
+ </para>
+ </introduction>
+
+ </developerConceptualDocument>
+</topic>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<topic id="4e89497f-70d3-4fb3-9c92-cfd8981ccfa9" revisionNumber="1">
+ <developerWalkthroughDocument
+ xmlns="http://ddue.schemas.microsoft.com/authoring/2003/5"
+ xmlns:xlink="http://www.w3.org/1999/xlink">
+
+ <introduction>
+ <para>
+ Hazel is a low level networking library that takes away a lot of the pain of writing sockets. Hazel provides the guarantee of connection orientated, message based communication across TCP, UDP and RUDP.
+ </para>
+ <para>
+ This guide will take you through the stages of writing a console based server and connecting to it from a console based client.
+ </para>
+ </introduction>
+
+ <!-- <prerequisites><content>Optional prerequisites info</content></prerequisites> -->
+
+ <section>
+ <title>Creating a Server</title>
+ <content>
+ <procedure>
+ <title>Creating a Listener</title>
+ <steps class="ordered">
+ <step>
+ <content>
+ <para>Create a new solution containing a Console project named "Server" (or at least something obvious).</para>
+ </content>
+ </step>
+ <step>
+ <content>
+ <para>Add a reference to Hazel.dll in the project.</para>
+ </content>
+ </step>
+ <step>
+ <content>
+ <para>Modify the default file to look like this, we'll go through each part individually.</para>
+ <code language="C#">
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Net;
+
+using Hazel;
+using Hazel.Tcp;
+
+namespace HazelExample
+{
+ class ServerExample
+ {
+ static ConnectionListener listener;
+
+ public static void Main(string[] args)
+ {
+ listener = new TcpConnectionListener(IPAddress.Any, 4296);
+
+ listener.NewConnection += NewConnectionHandler;
+
+ Console.WriteLine("Starting server!");
+
+ listener.Start();
+
+ Console.WriteLine("Press any key to continue...");
+
+ Console.ReadKey();
+
+ listener.Close();
+ }
+ }
+}
+ </code>
+ <para>
+ Firstly we need to tell the compiler that we are <codeInline>using Hazel;</codeInline> and <codeInline>using Hazel.Tcp;</codeInline> so we have access to Hazel's general and TCP specific types. Secondly we create a <codeEntityReference qualifyHint="false" linkText="ConnectionListener">T:Hazel.ConnectionListener</codeEntityReference>, these wait on a specified port and accept new clients to the server invoking the <codeEntityReference qualifyHint="false" linkText="NewConnection">E:Hazel.ConnectionListener.NewConnection</codeEntityReference> event each time a new client connects.
+ </para>
+ <para>
+ We then instruct the ConnectionListener to begin listening for new clients by calling <codeEntityReference qualifyHint="false" linkText="Start">M:ConnectionListener.Start</codeEntityReference> and then finally we close the listener using <codeEntityReference qualifyHint="false" linkText="Close">M:ConnectionListener.Close</codeEntityReference>.
+ </para>
+ <para>
+ In this circumstance it would be much better if we enclosed the listener within a <codeInline>using</codeInline> block as it implements IDisposable, however for the majority of use cases you are more likely to use the listener in this way. If you want to see it used in a <codeInline>using</codeInline> block then look at the unit tests.
+ </para>
+ <para>
+ If you want to use UDP instead of TCP then it is as simple as including the <codeInline>Hazel.Udp</codeInline> namespace and creating a <codeEntityReference qualifyHint="false" linkText="UdpConnectionListener">T:UdpConnectionListener</codeEntityReference> instead.
+ </para>
+ </content>
+ </step>
+ </steps>
+ </procedure>
+
+ <procedure>
+ <title>Handling Events</title>
+ <steps class="ordered">
+ <step>
+ <content>
+ <para>In the last example we subscribed to the <codeEntityReference qualifyHint="false" linkText="NewConnection">E:ConnectionListener.NewConnection</codeEntityReference> event but we never spsecified what to do in that event. Add the following method to your code.</para>
+ <code language="C#">
+static void NewConnectionHandler(object sender, NewConnectionEventArgs args)
+{
+ Console.WriteLine("New connection from " + args.Connection.EndPoint.ToString();
+
+ args.Connection.DataReceived += DataReceivedHandler;
+}
+ </code>
+ <para>This method is fairly simple, it follows the standard event handler delegate and take a sender (in this case it will be the ConnectionListener that called the event) and some args. The args contain a Connection which is the main object we use for communication with clients.</para>
+ <para>You can imagine this process in a similar way to TCP. You create a listener which creates sockets on the server side for each client socket that connects to it.</para>
+ <para>In this method we simply print out the IP of the client that just connected and then we subscribe to any data that this client sends us. You may also want to store the connection here for later reference as Hazel doesn't maintain a list of connection for you.</para>
+ </content>
+ </step>
+ <step>
+ <content>
+ <para>Once again we've got an undeclared event handler so lets fill that in now.</para>
+ <code language="C#">
+private static void DataReceivedHandler(object sender, DataEventArgs args)
+{
+ Connection connection = (Connection)sender;
+
+ Console.WriteLine("Received (" + string.Join<byte>(", ", args.Bytes) + ") from " + connection.EndPoint.ToString());
+
+ connection.SendBytes(args.Bytes, args.SendOption);
+
+ args.Recycle();
+}
+ </code>
+ <para>Again this method follows the standard event handler delegate and this time we make use of the sender parameter to get the connection that received the data. We then go on to print out the data received and then we send it back to the client using <codeEntityReference qualifyHint="false" linkText="SendBytes">M:ConnectionListener.SendBytes</codeEntityReference>. When we send we specify that the send option should be the same as the data that was received, we'll talk more about send options later.</para>
+ <para>You have also probably noticed that I didn't mention the <codeEntityReference qualifyHint="false" linkText="Recycle">M:DataEventArgs.Recycle</codeEntityReference> call. Recycle is an optional call that tells Hazel that it is now safe to use the object again rather than creating a new one and having to wait for the GC to collect the old object. If you are sending a lot of data then you will get less GC runs if you call Recycle but it is not necerssary to call it and if you dont the GC will collect it as normal. Also note that you should only call Recycle once you are done using the object otherwise the data inside it may change!</para>
+ </content>
+ </step>
+ </steps>
+ </procedure>
+
+ <para>In total, you should have something like this.</para>
+ <code language="C#">
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Net;
+
+using Hazel;
+using Hazel.Tcp;
+
+namespace HazelExample
+{
+ class ServerExample
+ {
+ static ConnectionListener listener;
+
+ public static void Main(string[] args)
+ {
+ listener = new TcpConnectionListener(IPAddress.Any, 4296);
+
+ listener.NewConnection += NewConnectionHandler;
+
+ Console.WriteLine("Starting server!");
+
+ listener.Start();
+
+ Console.WriteLine("Press any key to continue...");
+
+ Console.ReadKey();
+
+ listener.Close();
+ }
+
+ static void NewConnectionHandler(object sender, NewConnectionEventArgs args)
+ {
+ Console.WriteLine("New connection from " + args.Connection.EndPoint.ToString();
+
+ args.Connection.DataReceived += DataReceivedHandler;
+
+ args.Recycle();
+ }
+
+ private static void DataReceivedHandler(object sender, DataEventArgs args)
+ {
+ Connection connection = (Connection)sender;
+
+ Console.WriteLine("Received (" + string.Join<byte>(", ", args.Bytes) + ") from " + connection.EndPoint.ToString());
+
+ connection.SendBytes(args.Bytes, args.SendOption);
+
+ args.Recycle();
+ }
+ }
+}
+ </code>
+ </content>
+ </section>
+
+ <section>
+ <title>Creating a Client</title>
+ <content>
+ <procedure>
+ <title>Connecting to a Server</title>
+ <steps class="ordered">
+ <step>
+ <content>
+ <para>Create a new project in your solution for th client and add a reference to Hazel.dll.</para>
+ </content>
+ </step>
+
+ <step>
+ <content>
+ <para>Modify the default file to contain the following.</para>
+ <code language="C#">
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+using Hazel;
+using Hazel.Tcp;
+
+namespace HazelExample
+{
+ class ClientExample
+ {
+ static Connection connection;
+
+ public static void Main(string[] args)
+ {
+ NetworkEndPoint endPoint = new NetworkEndPoint("127.0.0.1", 4296);
+
+ connection = new TcpConnection(endPoint);
+
+ connection.DataReceived += DataReceived;
+
+ Console.WriteLine("Connecting!");
+
+ connection.Connect();
+
+ Console.WriteLine("Press any key to continue...");
+
+ Console.ReadKey();
+
+ connection.Close();
+ }
+ }
+}
+ </code>
+ <para>As you can see you simply create a <codeEntityReference qualifyHint="false" linkText="NetworkEndPoint">T:NetworkEndPoint</codeEntityReference> for the remote server and then pass it into a new <codeEntityReference qualifyHint="false" linkText="Connection">T:Connection</codeEntityReference>. Then you can setup any events needed and finally call <codeEntityReference qualifyHint="false" linkText="Connect">M:Connection.Connect</codeEntityReference> to begin the connection.</para>
+ <para>Finally we close the connection using <codeEntityReference qualifyHint="false" linkText="Close">M:Connection.Close</codeEntityReference>. Again, Connection implements IDisposable and so must be closed, if it is easier you could wrap it in a <codeInline>using</codeInline> block.</para>
+ <para>
+ If you are using UDP instead of TCP then include the <codeInline>Hazel.Udp</codeInline> namespace and create a <codeEntityReference qualifyHint="false" linkText="UdpClientConnection">T:UdpClientConnection</codeEntityReference> instead.
+ </para>
+ </content>
+ </step>
+ </steps>
+ </procedure>
+
+ <procedure>
+ <title>Handling Events</title>
+ <steps class="ordered">
+ <step>
+ <content>
+ <para>Add the following event handler to receive data, you'll notice that this is the same event we used in the server and so it will not be explained.</para>
+ <code language="C#">
+private static void DataReceived(object sender, DataEventArgs args)
+{
+ Console.WriteLine("Received (" + string.Join<byte>(", ", args.Bytes) + ") from " + connection.EndPoint.ToString());
+
+ args.Recycle();
+}
+ </code>
+ </content>
+ </step>
+ </steps>
+ </procedure>
+
+ <procedure>
+ <title>Sending Messages</title>
+ <steps class="ordered">
+ <step>
+ <content>
+ <para>Sending a message is the same for both the server and client, you simply call <codeEntityReference qualifyHint="false" linkText="SendBytes">M:Connection.SendBytes</codeEntityReference> on your connection.</para>
+ <para>Add the following line after the call to Connect</para>
+ <code>
+connection.SendBytes(new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 });
+ </code>
+ </content>
+ </step>
+ </steps>
+ </procedure>
+
+ <para>You should have something like this.</para>
+ <code language="C#">
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+using Hazel;
+using Hazel.Tcp;
+
+namespace HazelExample
+{
+ class ClientExample
+ {
+ static Connection connection;
+
+ public static void Main(string[] args)
+ {
+ NetworkEndPoint endPoint = new NetworkEndPoint("127.0.0.1", 4296);
+
+ connection = new TcpConnection(endPoint);
+
+ connection.DataReceived += DataReceived;
+
+ Console.WriteLine("Connecting!");
+
+ connection.Connect();
+
+ connection.SendBytes(new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 });
+
+ Console.WriteLine("Press any key to continue...");
+
+ Console.ReadKey();
+
+ connection.Close();
+ }
+
+ private static void DataReceived(object sender, DataEventArgs args)
+ {
+ Console.WriteLine("Received (" + string.Join<byte>(", ", args.Bytes) + ") from " + connection.EndPoint.ToString());
+
+ args.Recycle();
+ }
+ }
+}
+ </code>
+ </content>
+ </section>
+ </developerWalkthroughDocument>
+</topic>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<topic id="0ffa7468-4813-4fd0-ba65-2fa5436bc5ce" revisionNumber="1">
+ <developerConceptualDocument xmlns="http://ddue.schemas.microsoft.com/authoring/2003/5" xmlns:xlink="http://www.w3.org/1999/xlink">
+
+ <introduction>
+ <para>
+ In order to provide the guarantees that it does, Hazel augments each
+ transport protocol with its own meta data (or header data) that is
+ hidden from users.
+ </para>
+ </introduction>
+
+ </developerConceptualDocument>
+</topic>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<topic id="a3d0767d-2da3-45d2-80d7-b0c05698992f" revisionNumber="1">
+ <developerSDKTechnologyOverviewArchitectureDocument xmlns="http://ddue.schemas.microsoft.com/authoring/2003/5" xmlns:xlink="http://www.w3.org/1999/xlink">
+
+ <introduction>
+ <para>
+ Hazel's TCP protocol is fairly simple as TCP already provides connection
+ based communication and thus only needs a message system implementing.
+ </para>
+ </introduction>
+
+ <section>
+ <title>Header Data</title>
+ <content>
+ <para>
+ The TCP protocol is fairly simple interms of header. 4 bytes are
+ added to mark the number of bytes in each message.
+ </para>
+ <table>
+ <row>
+ <entry><para>Length (MSB)</para></entry>
+ <entry><para>Length</para></entry>
+ <entry><para>Length</para></entry>
+ <entry><para>Length (LSB)</para></entry>
+ <entry><para>Data...</para></entry>
+ </row>
+ </table>
+ <para>All header data in Hazel is encoded in big endian format.</para>
+ </content>
+ </section>
+
+ </developerSDKTechnologyOverviewArchitectureDocument>
+</topic>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<topic id="490c9f89-38c8-4ccf-8af1-683ba7fb0888" revisionNumber="1">
+ <developerSDKTechnologyOverviewArchitectureDocument xmlns="http://ddue.schemas.microsoft.com/authoring/2003/5" xmlns:xlink="http://www.w3.org/1999/xlink">
+
+ <introduction>
+ <para>
+ UDP provides message based communication however it is not connection
+ oriented nor does it have any built in functionality for reliable or
+ fragmented delivery of messages hence the implementation to cover this
+ if fairly complicated.
+ </para>
+ </introduction>
+
+ <section>
+ <title>Header Data</title>
+ <content>
+ <para>
+ The UDP protocol has multiple layers of header data depending on the
+ send options that are requested with the message.
+ </para>
+ <table>
+ <row>
+ <entry><para><legacyBold>Unreliable</legacyBold></para></entry>
+ <entry><para>Type</para></entry>
+ <entry><para>Data...</para></entry>
+ <entry><para> </para></entry>
+ <entry><para> </para></entry>
+ </row>
+ <row>
+ <entry><para><legacyBold>Reliable</legacyBold></para></entry>
+ <entry><para>Type</para></entry>
+ <entry><para>ID (MSB)</para></entry>
+ <entry><para>ID (LSB)</para></entry>
+ <entry><para>Data...</para></entry>
+ </row>
+ </table>
+ <para>All header data in Hazel is encoded in big endian format.</para>
+ <para>
+ In both of these, <quoteInline>Type</quoteInline> is an identifier
+ that holds the SendOption or another value indicating a control
+ packet of data. The most significant 4 bits of
+ <quoteInline>Type</quoteInline> are reserved for future use and the
+ least significant bits specify the send option flags for the message.
+ </para>
+ <table>
+ <tableHeader>
+ <row>
+ <entry><para>128</para></entry>
+ <entry><para>64</para></entry>
+ <entry><para>32</para></entry>
+ <entry><para>16</para></entry>
+ <entry><para>8</para></entry>
+ <entry><para>4</para></entry>
+ <entry><para>2</para></entry>
+ <entry><para>1</para></entry>
+ </row>
+ </tableHeader>
+ <row>
+ <entry><para>Reserved</para></entry>
+ <entry><para>Reserved</para></entry>
+ <entry><para>Reserved</para></entry>
+ <entry><para>Reserved</para></entry>
+ <entry><para>Control</para></entry>
+ <entry><para>Reserved</para></entry>
+ <entry><para>Fragmented</para></entry>
+ <entry><para>Reliable</para></entry>
+ </row>
+ </table>
+ <para>
+ <quoteInline>Reliable</quoteInline> and
+ <quoteInline>Fragmented</quoteInline> are both flags for their
+ respective send option, <quoteInline>Control</quoteInline>, if set,
+ specifies that the 3 least significant bits refer to a control code:
+ </para>
+ <table>
+ <row>
+ <entry><para>0</para></entry>
+ <entry><para>Hello</para></entry>
+ </row>
+ <row>
+ <entry><para>1</para></entry>
+ <entry><para>Disconnect</para></entry>
+ </row>
+ <row>
+ <entry><para>2</para></entry>
+ <entry><para>Acknowledgment</para></entry>
+ </row>
+ </table>
+ </content>
+ </section>
+
+ <section>
+ <title>Reliable Delivery</title>
+ <content>
+ <para>
+ To implement reliable delivery 2 ID bytes are sent which identify the
+ packet. When the receiver receives the data is replys with an
+ acknowledgement packet as follows:
+ </para>
+ <table>
+ <row>
+ <entry><para><legacyBold>Acknowledgement</legacyBold></para></entry>
+ <entry><para>Type</para></entry>
+ <entry><para>ID (MSB)</para></entry>
+ <entry><para>ID (LSB)</para></entry>
+ </row>
+ </table>
+ <para>
+ Where type is specifying an acknowledgement packet as laid out above.
+ </para>
+ <para>
+ If the sending client does not receive an acknowledgement after a
+ specific amount of time elapses from the pakcet being sent then it
+ resends that packet and thetime before the next resend of that packet
+ is doubled. When the sending client receives the acknowledgement is
+ should not resend the data again.
+ </para>
+ <para>
+ The receiving client must also ensure that it does not present the
+ same packet to the user twice but must acknowledge any packets it
+ receives, even if it has already received that packet, in case an
+ acknowledgement is lost.
+ </para>
+ <para>
+ After a specific number of resends without acknowledgement a sending
+ client may mark assume that communication has been interrupted and
+ thus mark the connection as disconnected.
+ </para>
+ </content>
+ </section>
+
+ <section>
+ <title>Keepalive packets</title>
+ <content>
+ <para>
+ Keepalive packets should be sent after a specific time has elapsed
+ since the last packet (either keepalive, acknowledgement or user sent)
+ was transmitted and should be sent using reliable delivery so that an
+ acknowledgement can be received to indicate communication has not been
+ lost. As packets that are not acknowledged should cause a
+ disconnection no additional logic is required for keepalives.
+ </para>
+ </content>
+ </section>
+ </developerSDKTechnologyOverviewArchitectureDocument>
+</topic>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<topic id="852623d7-5d68-47e2-b5db-76498da44f95" revisionNumber="1">
+ <developerConceptualDocument xmlns="http://ddue.schemas.microsoft.com/authoring/2003/5" xmlns:xlink="http://www.w3.org/1999/xlink">
+
+ <introduction>
+ <para>
+ Underneath Hazel there are a lot of technicalities, this section will
+ help outline those details so you understand Hazel's implementations
+ better.
+ </para>
+ </introduction>
+
+ </developerConceptualDocument>
+</topic>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<topic id="79b24ac4-3a49-4f2d-b074-019040fd7541" revisionNumber="1">
+ <developerConceptualDocument xmlns="http://ddue.schemas.microsoft.com/authoring/2003/5" xmlns:xlink="http://www.w3.org/1999/xlink">
+ <introduction>
+ <para>This is a sample conceptual topic. You can use this as a starting point for adding more conceptual
+content to your help project.</para>
+ </introduction>
+
+ <section>
+ <title>Getting Started</title>
+ <content>
+ <para>To get started, add a documentation source to the project (a Visual Studio solution, project, or
+assembly and XML comments file). See the <legacyBold>Getting Started</legacyBold> topics in the Sandcastle Help
+File Builder's help file for more information. The following default items are included in this project:</para>
+
+ <list class="bullet">
+ <listItem>
+ <para><localUri>ContentLayout.content</localUri> - Use the content layout file to manage the
+conceptual content in the project and define its layout in the table of contents.</para>
+ </listItem>
+
+ <listItem>
+ <para>The <localUri>.\media</localUri> folder - Place images in this folder that you will reference
+from conceptual content using <codeInline>medialLink</codeInline> or <codeInline>mediaLinkInline</codeInline>
+elements. If you will not have any images in the file, you may remove this folder.</para>
+ </listItem>
+
+ <listItem>
+ <para>The <localUri>.\icons</localUri> folder - This contains a default logo for the help file. You
+may replace it or remove it and the folder if not wanted. If removed or if you change the file name, update
+the <ui>Transform Args</ui> project properties page by removing or changing the filename in the
+<codeInline>logoFile</codeInline> transform argument. Note that unlike images referenced from conceptual topics,
+the logo file should have its <legacyBold>BuildAction</legacyBold> property set to <codeInline>Content</codeInline>.</para>
+ </listItem>
+
+ <listItem>
+ <para>The <localUri>.\Content</localUri> folder - Use this to store your conceptual topics. You may
+name the files and organize them however you like. One suggestion is to lay the files out on disk as you have
+them in the content layout file as shown in this project but the choice is yours. Files can be added via the
+Solution Explorer or from within the content layout file editor. Files must appear in the content layout file
+in order to be compiled into the help file.</para>
+ </listItem>
+ </list>
+
+ <para>See the <legacyBold>Conceptual Content</legacyBold> topics in the Sandcastle Help File Builder's
+help file for more information. See the <legacyBold> Sandcastle MAML Guide</legacyBold> for details on Microsoft
+Assistance Markup Language (MAML) which is used to create these topics.</para>
+ </content>
+ </section>
+
+ <relatedTopics>
+ <link xlink:href="c960edda-5507-4e9d-af3d-26a418bdf007" />
+ </relatedTopics>
+ </developerConceptualDocument>
+</topic>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<Topics>
+ <Topic id="79b24ac4-3a49-4f2d-b074-019040fd7541" visible="True" isDefault="true" isSelected="true" title="Welcome to the [TODO: Add project name]">
+ <HelpKeywords>
+ <HelpKeyword index="K" term="Welcome" />
+ </HelpKeywords>
+ </Topic>
+ <Topic id="c960edda-5507-4e9d-af3d-26a418bdf007" visible="True" isExpanded="true" title="Version History">
+ <HelpKeywords>
+ <HelpKeyword index="K" term="version, history" />
+ </HelpKeywords>
+ <Topic id="62867feb-6930-4bbe-863b-7e97d4735c73" visible="True" title="Version 1.0.0.0">
+ <HelpKeywords>
+ <HelpKeyword index="K" term="version, 1.0.0.0" />
+ </HelpKeywords>
+ </Topic>
+ </Topic>
+</Topics>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <PropertyGroup>
+ <!-- The configuration and platform will be used to determine which assemblies to include from solution and
+ project documentation sources -->
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <SchemaVersion>2.0</SchemaVersion>
+ <ProjectGuid>b90bb577-2080-4c22-ae86-fb0ccfb40920</ProjectGuid>
+ <SHFBSchemaVersion>2015.6.5.0</SHFBSchemaVersion>
+ <!-- AssemblyName, Name, and RootNamespace are not used by SHFB but Visual Studio adds them anyway -->
+ <AssemblyName>Hazel.Documentation</AssemblyName>
+ <RootNamespace>Hazel.Documentation</RootNamespace>
+ <Name>Hazel.Documentation</Name>
+ <!-- SHFB properties -->
+ <FrameworkVersion>.NET Framework 4.5</FrameworkVersion>
+ <OutputPath>.\Help\</OutputPath>
+ <HtmlHelpName>Hazel.Documentation</HtmlHelpName>
+ <Language>en-US</Language>
+ <TransformComponentArguments>
+ <Argument Key="logoFile" Value="Help.png" xmlns="" />
+ <Argument Key="logoHeight" Value="" xmlns="" />
+ <Argument Key="logoWidth" Value="" xmlns="" />
+ <Argument Key="logoAltText" Value="" xmlns="" />
+ <Argument Key="logoPlacement" Value="left" xmlns="" />
+ <Argument Key="logoAlignment" Value="left" xmlns="" />
+ <Argument Key="maxVersionParts" Value="" xmlns="" />
+ </TransformComponentArguments>
+ <DocumentationSources>
+ <DocumentationSource sourceFile="..\Hazel\Hazel.csproj" xmlns="" />
+ </DocumentationSources>
+ </PropertyGroup>
+ <!-- There are no properties for these groups. AnyCPU needs to appear in order for Visual Studio to perform
+ the build. The others are optional common platform types that may appear. -->
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x64' ">
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x64' ">
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|Win32' ">
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|Win32' ">
+ </PropertyGroup>
+ <ItemGroup>
+ <Folder Include="Content" />
+ <Folder Include="Content\VersionHistory" />
+ <Folder Include="icons" />
+ <Folder Include="media" />
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="Content\VersionHistory\v1.0.0.0.aml" />
+ <None Include="Content\VersionHistory\VersionHistory.aml" />
+ <None Include="Content\Welcome.aml" />
+ </ItemGroup>
+ <ItemGroup>
+ <ContentLayout Include="ContentLayout.content" />
+ </ItemGroup>
+ <ItemGroup>
+ <Content Include="icons\Help.png" />
+ </ItemGroup>
+ <!-- Import the SHFB build targets -->
+ <Import Project="$(SHFBROOT)\SandcastleHelpFileBuilder.targets" />
+ <!-- The pre-build and post-build event properties must appear *after* the targets file import in order to be
+ evaluated correctly. -->
+ <PropertyGroup>
+ <PreBuildEvent>
+ </PreBuildEvent>
+ <PostBuildEvent>
+ </PostBuildEvent>
+ <RunPostBuildEvent>OnBuildSuccess</RunPostBuildEvent>
+ </PropertyGroup>
+</Project>
\ No newline at end of file
Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio 2013
-VisualStudioVersion = 12.0.31101.0
+# Visual Studio 14
+VisualStudioVersion = 14.0.25420.1
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Hazel", "Hazel\Hazel.csproj", "{02CFBD30-D77D-400F-94B2-700F60EFDD7F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Hazel.UnitTests", "Hazel.UnitTests\Hazel.UnitTests.csproj", "{1394E4CA-E17A-42F5-9216-8046ACA8D16B}"
EndProject
+Project("{7CF6DF6D-3B04-46F8-A40B-537D21BCA0B4}") = "Hazel.Documentation", "Hazel.Documentation\Hazel.Documentation.shfbproj", "{B90BB577-2080-4C22-AE86-FB0CCFB40920}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
{1394E4CA-E17A-42F5-9216-8046ACA8D16B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1394E4CA-E17A-42F5-9216-8046ACA8D16B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1394E4CA-E17A-42F5-9216-8046ACA8D16B}.Release|Any CPU.Build.0 = Release|Any CPU
+ {B90BB577-2080-4C22-AE86-FB0CCFB40920}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {B90BB577-2080-4C22-AE86-FB0CCFB40920}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {B90BB577-2080-4C22-AE86-FB0CCFB40920}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {B90BB577-2080-4C22-AE86-FB0CCFB40920}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
+++ /dev/null
-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, DataReceivedEventArgs 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();
- }
- }
-}
+++ /dev/null
-class TcpListenerExample
-{
- static void Main(string[] args)
- {
- //Setup listener
- using (TcpConnectionListener listener = new TcpConnectionListener(new NetworkEndPoint(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();
- }
- }
-}
+++ /dev/null
-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, DataReceivedEventArgs 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();
- }
- }
-}
+++ /dev/null
-class UdpListenerExample
-{
- static void Main(string[] args)
- {
- //Setup listener
- using (UdpConnectionListener listener = new UdpConnectionListener(new NetworkEndPoint(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();
- }
- }
-}
+++ /dev/null
-<?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="System.Threading.ThreadPool">ThreadPool</see> 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="Connection.State"/> property for information on whether a connection is connected or not.
- </para>
- </item>
-</docs>
\ No newline at end of file
+++ /dev/null
-<?xml version="1.0" encoding="utf-8"?>
-<Topics>
- <Topic id="a9f1cfac-8d7d-4668-af2f-60b588018519" visible="True" isSelected="true" title="Introduction" />
- <Topic id="4e89497f-70d3-4fb3-9c92-cfd8981ccfa9" visible="True" title="Quickstart" />
- <Topic id="852623d7-5d68-47e2-b5db-76498da44f95" visible="True" isExpanded="true" title="Technical Details">
- <Topic id="0ffa7468-4813-4fd0-ba65-2fa5436bc5ce" visible="True" isExpanded="true" title="Protocols">
- <Topic id="a3d0767d-2da3-45d2-80d7-b0c05698992f" visible="True" />
- <Topic id="490c9f89-38c8-4ccf-8af1-683ba7fb0888" visible="True" />
- </Topic>
- </Topic>
-</Topics>
\ No newline at end of file
+++ /dev/null
-<?xml version="1.0" encoding="utf-8"?>
-<topic id="a9f1cfac-8d7d-4668-af2f-60b588018519" revisionNumber="1">
- <developerConceptualDocument xmlns="http://ddue.schemas.microsoft.com/authoring/2003/5" xmlns:xlink="http://www.w3.org/1999/xlink">
-
- <introduction>
- <para>
- Welcome to the Hazel documentation! Here you will find technical
- details, API references and tutorials for getting started with Hazel.
- </para>
- <para>
- Hazel Networking is an open source low level networking library for C# providing
- connection orientated, message bassed communication via TCP, UDP and
- RUDP. You can download it from Github
- <externalLink>
- <linkText>here</linkText>
- <linkUri>https://github.com/DarkRiftNetworking/Hazel-Networking</linkUri>
- <linkTarget>_blank</linkTarget>
- </externalLink>
- .
- </para>
- </introduction>
-
- </developerConceptualDocument>
-</topic>
\ No newline at end of file
+++ /dev/null
-<?xml version="1.0" encoding="utf-8"?>
-<topic id="4e89497f-70d3-4fb3-9c92-cfd8981ccfa9" revisionNumber="1">
- <developerWalkthroughDocument
- xmlns="http://ddue.schemas.microsoft.com/authoring/2003/5"
- xmlns:xlink="http://www.w3.org/1999/xlink">
-
- <introduction>
- <para>
- Hazel is a low level networking library that takes away a lot of the pain of writing sockets. Hazel provides the guarantee of connection orientated, message based communication across TCP, UDP and RUDP.
- </para>
- <para>
- This guide will take you through the stages of writing a console based server and connecting to it from a console based client.
- </para>
- </introduction>
-
- <!-- <prerequisites><content>Optional prerequisites info</content></prerequisites> -->
-
- <section>
- <title>Creating a Server</title>
- <content>
- <procedure>
- <title>Creating a Listener</title>
- <steps class="ordered">
- <step>
- <content>
- <para>Create a new solution containing a Console project named "Server" (or at least something obvious).</para>
- </content>
- </step>
- <step>
- <content>
- <para>Add a reference to Hazel.dll in the project.</para>
- </content>
- </step>
- <step>
- <content>
- <para>Modify the default file to look like this, we'll go through each part individually.</para>
- <code language="C#">
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using System.Net;
-
-using Hazel;
-using Hazel.Tcp;
-
-namespace HazelExample
-{
- class ServerExample
- {
- static ConnectionListener listener;
-
- public static void Main(string[] args)
- {
- listener = new TcpConnectionListener(IPAddress.Any, 4296);
-
- listener.NewConnection += NewConnectionHandler;
-
- Console.WriteLine("Starting server!");
-
- listener.Start();
-
- Console.WriteLine("Press any key to continue...");
-
- Console.ReadKey();
-
- listener.Close();
- }
- }
-}
- </code>
- <para>
- Firstly we need to tell the compiler that we are <codeInline>using Hazel;</codeInline> and <codeInline>using Hazel.Tcp;</codeInline> so we have access to Hazel's general and TCP specific types. Secondly we create a <codeEntityReference qualifyHint="false" linkText="ConnectionListener">T:Hazel.ConnectionListener</codeEntityReference>, these wait on a specified port and accept new clients to the server invoking the <codeEntityReference qualifyHint="false" linkText="NewConnection">E:Hazel.ConnectionListener.NewConnection</codeEntityReference> event each time a new client connects.
- </para>
- <para>
- We then instruct the ConnectionListener to begin listening for new clients by calling <codeEntityReference qualifyHint="false" linkText="Start">M:ConnectionListener.Start</codeEntityReference> and then finally we close the listener using <codeEntityReference qualifyHint="false" linkText="Close">M:ConnectionListener.Close</codeEntityReference>.
- </para>
- <para>
- In this circumstance it would be much better if we enclosed the listener within a <codeInline>using</codeInline> block as it implements IDisposable, however for the majority of use cases you are more likely to use the listener in this way. If you want to see it used in a <codeInline>using</codeInline> block then look at the unit tests.
- </para>
- <para>
- If you want to use UDP instead of TCP then it is as simple as including the <codeInline>Hazel.Udp</codeInline> namespace and creating a <codeEntityReference qualifyHint="false" linkText="UdpConnectionListener">T:UdpConnectionListener</codeEntityReference> instead.
- </para>
- </content>
- </step>
- </steps>
- </procedure>
-
- <procedure>
- <title>Handling Events</title>
- <steps class="ordered">
- <step>
- <content>
- <para>In the last example we subscribed to the <codeEntityReference qualifyHint="false" linkText="NewConnection">E:ConnectionListener.NewConnection</codeEntityReference> event but we never spsecified what to do in that event. Add the following method to your code.</para>
- <code language="C#">
-static void NewConnectionHandler(object sender, NewConnectionEventArgs args)
-{
- Console.WriteLine("New connection from " + args.Connection.EndPoint.ToString();
-
- args.Connection.DataReceived += DataReceivedHandler;
-}
- </code>
- <para>This method is fairly simple, it follows the standard event handler delegate and take a sender (in this case it will be the ConnectionListener that called the event) and some args. The args contain a Connection which is the main object we use for communication with clients.</para>
- <para>You can imagine this process in a similar way to TCP. You create a listener which creates sockets on the server side for each client socket that connects to it.</para>
- <para>In this method we simply print out the IP of the client that just connected and then we subscribe to any data that this client sends us. You may also want to store the connection here for later reference as Hazel doesn't maintain a list of connection for you.</para>
- </content>
- </step>
- <step>
- <content>
- <para>Once again we've got an undeclared event handler so lets fill that in now.</para>
- <code language="C#">
-private static void DataReceivedHandler(object sender, DataEventArgs args)
-{
- Connection connection = (Connection)sender;
-
- Console.WriteLine("Received (" + string.Join<byte>(", ", args.Bytes) + ") from " + connection.EndPoint.ToString());
-
- connection.SendBytes(args.Bytes, args.SendOption);
-
- args.Recycle();
-}
- </code>
- <para>Again this method follows the standard event handler delegate and this time we make use of the sender parameter to get the connection that received the data. We then go on to print out the data received and then we send it back to the client using <codeEntityReference qualifyHint="false" linkText="SendBytes">M:ConnectionListener.SendBytes</codeEntityReference>. When we send we specify that the send option should be the same as the data that was received, we'll talk more about send options later.</para>
- <para>You have also probably noticed that I didn't mention the <codeEntityReference qualifyHint="false" linkText="Recycle">M:DataEventArgs.Recycle</codeEntityReference> call. Recycle is an optional call that tells Hazel that it is now safe to use the object again rather than creating a new one and having to wait for the GC to collect the old object. If you are sending a lot of data then you will get less GC runs if you call Recycle but it is not necerssary to call it and if you dont the GC will collect it as normal. Also note that you should only call Recycle once you are done using the object otherwise the data inside it may change!</para>
- </content>
- </step>
- </steps>
- </procedure>
-
- <para>In total, you should have something like this.</para>
- <code language="C#">
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using System.Net;
-
-using Hazel;
-using Hazel.Tcp;
-
-namespace HazelExample
-{
- class ServerExample
- {
- static ConnectionListener listener;
-
- public static void Main(string[] args)
- {
- listener = new TcpConnectionListener(IPAddress.Any, 4296);
-
- listener.NewConnection += NewConnectionHandler;
-
- Console.WriteLine("Starting server!");
-
- listener.Start();
-
- Console.WriteLine("Press any key to continue...");
-
- Console.ReadKey();
-
- listener.Close();
- }
-
- static void NewConnectionHandler(object sender, NewConnectionEventArgs args)
- {
- Console.WriteLine("New connection from " + args.Connection.EndPoint.ToString();
-
- args.Connection.DataReceived += DataReceivedHandler;
-
- args.Recycle();
- }
-
- private static void DataReceivedHandler(object sender, DataEventArgs args)
- {
- Connection connection = (Connection)sender;
-
- Console.WriteLine("Received (" + string.Join<byte>(", ", args.Bytes) + ") from " + connection.EndPoint.ToString());
-
- connection.SendBytes(args.Bytes, args.SendOption);
-
- args.Recycle();
- }
- }
-}
- </code>
- </content>
- </section>
-
- <section>
- <title>Creating a Client</title>
- <content>
- <procedure>
- <title>Connecting to a Server</title>
- <steps class="ordered">
- <step>
- <content>
- <para>Create a new project in your solution for th client and add a reference to Hazel.dll.</para>
- </content>
- </step>
-
- <step>
- <content>
- <para>Modify the default file to contain the following.</para>
- <code language="C#">
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-using Hazel;
-using Hazel.Tcp;
-
-namespace HazelExample
-{
- class ClientExample
- {
- static Connection connection;
-
- public static void Main(string[] args)
- {
- NetworkEndPoint endPoint = new NetworkEndPoint("127.0.0.1", 4296);
-
- connection = new TcpConnection(endPoint);
-
- connection.DataReceived += DataReceived;
-
- Console.WriteLine("Connecting!");
-
- connection.Connect();
-
- Console.WriteLine("Press any key to continue...");
-
- Console.ReadKey();
-
- connection.Close();
- }
- }
-}
- </code>
- <para>As you can see you simply create a <codeEntityReference qualifyHint="false" linkText="NetworkEndPoint">T:NetworkEndPoint</codeEntityReference> for the remote server and then pass it into a new <codeEntityReference qualifyHint="false" linkText="Connection">T:Connection</codeEntityReference>. Then you can setup any events needed and finally call <codeEntityReference qualifyHint="false" linkText="Connect">M:Connection.Connect</codeEntityReference> to begin the connection.</para>
- <para>Finally we close the connection using <codeEntityReference qualifyHint="false" linkText="Close">M:Connection.Close</codeEntityReference>. Again, Connection implements IDisposable and so must be closed, if it is easier you could wrap it in a <codeInline>using</codeInline> block.</para>
- <para>
- If you are using UDP instead of TCP then include the <codeInline>Hazel.Udp</codeInline> namespace and create a <codeEntityReference qualifyHint="false" linkText="UdpClientConnection">T:UdpClientConnection</codeEntityReference> instead.
- </para>
- </content>
- </step>
- </steps>
- </procedure>
-
- <procedure>
- <title>Handling Events</title>
- <steps class="ordered">
- <step>
- <content>
- <para>Add the following event handler to receive data, you'll notice that this is the same event we used in the server and so it will not be explained.</para>
- <code language="C#">
-private static void DataReceived(object sender, DataEventArgs args)
-{
- Console.WriteLine("Received (" + string.Join<byte>(", ", args.Bytes) + ") from " + connection.EndPoint.ToString());
-
- args.Recycle();
-}
- </code>
- </content>
- </step>
- </steps>
- </procedure>
-
- <procedure>
- <title>Sending Messages</title>
- <steps class="ordered">
- <step>
- <content>
- <para>Sending a message is the same for both the server and client, you simply call <codeEntityReference qualifyHint="false" linkText="SendBytes">M:Connection.SendBytes</codeEntityReference> on your connection.</para>
- <para>Add the following line after the call to Connect</para>
- <code>
-connection.SendBytes(new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 });
- </code>
- </content>
- </step>
- </steps>
- </procedure>
-
- <para>You should have something like this.</para>
- <code language="C#">
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-using Hazel;
-using Hazel.Tcp;
-
-namespace HazelExample
-{
- class ClientExample
- {
- static Connection connection;
-
- public static void Main(string[] args)
- {
- NetworkEndPoint endPoint = new NetworkEndPoint("127.0.0.1", 4296);
-
- connection = new TcpConnection(endPoint);
-
- connection.DataReceived += DataReceived;
-
- Console.WriteLine("Connecting!");
-
- connection.Connect();
-
- connection.SendBytes(new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 });
-
- Console.WriteLine("Press any key to continue...");
-
- Console.ReadKey();
-
- connection.Close();
- }
-
- private static void DataReceived(object sender, DataEventArgs args)
- {
- Console.WriteLine("Received (" + string.Join<byte>(", ", args.Bytes) + ") from " + connection.EndPoint.ToString());
-
- args.Recycle();
- }
- }
-}
- </code>
- </content>
- </section>
- </developerWalkthroughDocument>
-</topic>
\ No newline at end of file
+++ /dev/null
-<?xml version="1.0" encoding="utf-8"?>
-<topic id="0ffa7468-4813-4fd0-ba65-2fa5436bc5ce" revisionNumber="1">
- <developerConceptualDocument xmlns="http://ddue.schemas.microsoft.com/authoring/2003/5" xmlns:xlink="http://www.w3.org/1999/xlink">
-
- <introduction>
- <para>
- In order to provide the guarantees that it does, Hazel augments each
- transport protocol with its own meta data (or header data) that is
- hidden from users.
- </para>
- </introduction>
-
- </developerConceptualDocument>
-</topic>
\ No newline at end of file
+++ /dev/null
-<?xml version="1.0" encoding="utf-8"?>
-<topic id="a3d0767d-2da3-45d2-80d7-b0c05698992f" revisionNumber="1">
- <developerSDKTechnologyOverviewArchitectureDocument xmlns="http://ddue.schemas.microsoft.com/authoring/2003/5" xmlns:xlink="http://www.w3.org/1999/xlink">
-
- <introduction>
- <para>
- Hazel's TCP protocol is fairly simple as TCP already provides connection
- based communication and thus only needs a message system implementing.
- </para>
- </introduction>
-
- <section>
- <title>Header Data</title>
- <content>
- <para>
- The TCP protocol is fairly simple interms of header. 4 bytes are
- added to mark the number of bytes in each message.
- </para>
- <table>
- <row>
- <entry><para>Length (MSB)</para></entry>
- <entry><para>Length</para></entry>
- <entry><para>Length</para></entry>
- <entry><para>Length (LSB)</para></entry>
- <entry><para>Data...</para></entry>
- </row>
- </table>
- <para>All header data in Hazel is encoded in big endian format.</para>
- </content>
- </section>
-
- </developerSDKTechnologyOverviewArchitectureDocument>
-</topic>
\ No newline at end of file
+++ /dev/null
-<?xml version="1.0" encoding="utf-8"?>
-<topic id="490c9f89-38c8-4ccf-8af1-683ba7fb0888" revisionNumber="1">
- <developerSDKTechnologyOverviewArchitectureDocument xmlns="http://ddue.schemas.microsoft.com/authoring/2003/5" xmlns:xlink="http://www.w3.org/1999/xlink">
-
- <introduction>
- <para>
- UDP provides message based communication however it is not connection
- oriented nor does it have any built in functionality for reliable or
- fragmented delivery of messages hence the implementation to cover this
- if fairly complicated.
- </para>
- </introduction>
-
- <section>
- <title>Header Data</title>
- <content>
- <para>
- The UDP protocol has multiple layers of header data depending on the
- send options that are requested with the message.
- </para>
- <table>
- <row>
- <entry><para><legacyBold>Unreliable</legacyBold></para></entry>
- <entry><para>Type</para></entry>
- <entry><para>Data...</para></entry>
- <entry><para> </para></entry>
- <entry><para> </para></entry>
- </row>
- <row>
- <entry><para><legacyBold>Reliable</legacyBold></para></entry>
- <entry><para>Type</para></entry>
- <entry><para>ID (MSB)</para></entry>
- <entry><para>ID (LSB)</para></entry>
- <entry><para>Data...</para></entry>
- </row>
- </table>
- <para>All header data in Hazel is encoded in big endian format.</para>
- <para>
- In both of these, <quoteInline>Type</quoteInline> is an identifier
- that holds the SendOption or another value indicating a control
- packet of data. The most significant 4 bits of
- <quoteInline>Type</quoteInline> are reserved for future use and the
- least significant bits specify the send option flags for the message.
- </para>
- <table>
- <tableHeader>
- <row>
- <entry><para>128</para></entry>
- <entry><para>64</para></entry>
- <entry><para>32</para></entry>
- <entry><para>16</para></entry>
- <entry><para>8</para></entry>
- <entry><para>4</para></entry>
- <entry><para>2</para></entry>
- <entry><para>1</para></entry>
- </row>
- </tableHeader>
- <row>
- <entry><para>Reserved</para></entry>
- <entry><para>Reserved</para></entry>
- <entry><para>Reserved</para></entry>
- <entry><para>Reserved</para></entry>
- <entry><para>Control</para></entry>
- <entry><para>Reserved</para></entry>
- <entry><para>Fragmented</para></entry>
- <entry><para>Reliable</para></entry>
- </row>
- </table>
- <para>
- <quoteInline>Reliable</quoteInline> and
- <quoteInline>Fragmented</quoteInline> are both flags for their
- respective send option, <quoteInline>Control</quoteInline>, if set,
- specifies that the 3 least significant bits refer to a control code:
- </para>
- <table>
- <row>
- <entry><para>0</para></entry>
- <entry><para>Hello</para></entry>
- </row>
- <row>
- <entry><para>1</para></entry>
- <entry><para>Disconnect</para></entry>
- </row>
- <row>
- <entry><para>2</para></entry>
- <entry><para>Acknowledgment</para></entry>
- </row>
- </table>
- </content>
- </section>
-
- <section>
- <title>Reliable Delivery</title>
- <content>
- <para>
- To implement reliable delivery 2 ID bytes are sent which identify the
- packet. When the receiver receives the data is replys with an
- acknowledgement packet as follows:
- </para>
- <table>
- <row>
- <entry><para><legacyBold>Acknowledgement</legacyBold></para></entry>
- <entry><para>Type</para></entry>
- <entry><para>ID (MSB)</para></entry>
- <entry><para>ID (LSB)</para></entry>
- </row>
- </table>
- <para>
- Where type is specifying an acknowledgement packet as laid out above.
- </para>
- <para>
- If the sending client does not receive an acknowledgement after a
- specific amount of time elapses from the pakcet being sent then it
- resends that packet and thetime before the next resend of that packet
- is doubled. When the sending client receives the acknowledgement is
- should not resend the data again.
- </para>
- <para>
- The receiving client must also ensure that it does not present the
- same packet to the user twice but must acknowledge any packets it
- receives, even if it has already received that packet, in case an
- acknowledgement is lost.
- </para>
- <para>
- After a specific number of resends without acknowledgement a sending
- client may mark assume that communication has been interrupted and
- thus mark the connection as disconnected.
- </para>
- </content>
- </section>
-
- <section>
- <title>Keepalive packets</title>
- <content>
- <para>
- Keepalive packets should be sent after a specific time has elapsed
- since the last packet (either keepalive, acknowledgement or user sent)
- was transmitted and should be sent using reliable delivery so that an
- acknowledgement can be received to indicate communication has not been
- lost. As packets that are not acknowledged should cause a
- disconnection no additional logic is required for keepalives.
- </para>
- </content>
- </section>
- </developerSDKTechnologyOverviewArchitectureDocument>
-</topic>
\ No newline at end of file
+++ /dev/null
-<?xml version="1.0" encoding="utf-8"?>
-<topic id="852623d7-5d68-47e2-b5db-76498da44f95" revisionNumber="1">
- <developerConceptualDocument xmlns="http://ddue.schemas.microsoft.com/authoring/2003/5" xmlns:xlink="http://www.w3.org/1999/xlink">
-
- <introduction>
- <para>
- Underneath Hazel there are a lot of technicalities, this section will
- help outline those details so you understand Hazel's implementations
- better.
- </para>
- </introduction>
-
- </developerConceptualDocument>
-</topic>
\ No newline at end of file
+++ /dev/null
-<?xml version="1.0" encoding="utf-8"?>
-<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
- <PropertyGroup>
- <!-- The configuration and platform will be used to determine which assemblies to include from solution and
- project documentation sources -->
- <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
- <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
- <SchemaVersion>2.0</SchemaVersion>
- <ProjectGuid>{359995e0-bef3-42dd-800f-20368ff5fabb}</ProjectGuid>
- <SHFBSchemaVersion>2015.6.5.0</SHFBSchemaVersion>
- <!-- AssemblyName, Name, and RootNamespace are not used by SHFB but Visual Studio adds them anyway -->
- <AssemblyName>Documentation</AssemblyName>
- <RootNamespace>Documentation</RootNamespace>
- <Name>Documentation</Name>
- <!-- SHFB properties -->
- <FrameworkVersion>.NET Framework 4.5</FrameworkVersion>
- <OutputPath>.\Help\</OutputPath>
- <HtmlHelpName>Documentation</HtmlHelpName>
- <Language>en-US</Language>
- <SaveComponentCacheCapacity>100</SaveComponentCacheCapacity>
- <BuildAssemblerVerbosity>OnlyWarningsAndErrors</BuildAssemblerVerbosity>
- <HelpFileFormat>Website</HelpFileFormat>
- <IndentHtml>False</IndentHtml>
- <KeepLogFile>True</KeepLogFile>
- <DisableCodeBlockComponent>False</DisableCodeBlockComponent>
- <CleanIntermediates>True</CleanIntermediates>
- <DocumentationSources>
- <DocumentationSource sourceFile="..\bin\Release\Hazel.dll" />
-<DocumentationSource sourceFile="..\bin\Release\Hazel.xml" /></DocumentationSources>
- <HelpFileVersion>1.0.0.0</HelpFileVersion>
- <MaximumGroupParts>2</MaximumGroupParts>
- <NamespaceGrouping>False</NamespaceGrouping>
- <SyntaxFilters>C#</SyntaxFilters>
- <SdkLinkTarget>Blank</SdkLinkTarget>
- <RootNamespaceContainer>False</RootNamespaceContainer>
- <PresentationStyle>VS2013</PresentationStyle>
- <Preliminary>False</Preliminary>
- <NamingMethod>Guid</NamingMethod>
- <HelpTitle>Hazel Networking</HelpTitle>
- <ContentPlacement>AboveNamespaces</ContentPlacement>
- <NamespaceSummaries>
- <NamespaceSummaryItem name="Hazel" isDocumented="True" xmlns="">The Hazel namespace contains various classes used across the different communication methods implemented.</NamespaceSummaryItem>
-<NamespaceSummaryItem name="Hazel.Tcp" isDocumented="True" xmlns="">Namespace for classes relating to communication via TCP.</NamespaceSummaryItem>
-<NamespaceSummaryItem name="Hazel.Udp" isDocumented="True" xmlns="">Namespace for classes relating to communication via TCP.</NamespaceSummaryItem></NamespaceSummaries>
- </PropertyGroup>
- <!-- There are no properties for these groups. AnyCPU needs to appear in order for Visual Studio to perform
- the build. The others are optional common platform types that may appear. -->
- <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
- </PropertyGroup>
- <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
- </PropertyGroup>
- <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
- </PropertyGroup>
- <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
- </PropertyGroup>
- <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x64' ">
- </PropertyGroup>
- <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x64' ">
- </PropertyGroup>
- <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|Win32' ">
- </PropertyGroup>
- <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|Win32' ">
- </PropertyGroup>
- <!-- Import the SHFB build targets -->
- <Import Project="$(SHFBROOT)\SandcastleHelpFileBuilder.targets" />
- <!-- The pre-build and post-build event properties must appear *after* the targets file import in order to be
- evaluated correctly. -->
- <PropertyGroup>
- <PreBuildEvent>
- </PreBuildEvent>
- <PostBuildEvent>
- </PostBuildEvent>
- <RunPostBuildEvent>OnBuildSuccess</RunPostBuildEvent>
- </PropertyGroup>
- <ItemGroup>
- <None Include="Documentation\Introduction.aml" />
- <None Include="Documentation\Technical Details\Protocols\TCP.aml" />
- <None Include="Documentation\Technical Details\Protocols\UDP-RUDP.aml" />
- <None Include="Documentation\Technical Details\Protocols\TCP.aml" />
- <None Include="Documentation\Technical Details\Technical Details.aml" />
- <None Include="Documentation\Technical Details\Protocols\Protocols.aml" />
- <None Include="Documentation\Quickstart.aml" />
- </ItemGroup>
- <ItemGroup>
- <Folder Include="Documentation\Technical Details\Protocols\" />
- <Folder Include="Documentation\Technical Details\" />
- <Folder Include="Documentation\" />
- </ItemGroup>
- <ItemGroup>
- <ContentLayout Include="Documentation\Content Layout.content" />
- </ItemGroup>
- <ItemGroup>
- <ProjectReference Include="..\Hazel.csproj">
- <Name>Hazel</Name>
- <Project>{02CFBD30-D77D-400F-94B2-700F60EFDD7F}</Project>
- </ProjectReference>
- </ItemGroup>
-</Project>
\ No newline at end of file
+++ /dev/null
-# Hey!
-
-## If you're looking for documentation go [here](http://www.darkriftnetworking.com/docs)! This is the source for the documentation!