Click or drag to resize

Quickstart

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.

This guide will take you through the stages of writing a console based server and connecting to it from a console based client.

Creating a Server

Creating a Listener

  1. Create a new solution containing a Console project named "Server" (or at least something obvious).

  2. Add a reference to Hazel.dll in the project.

  3. Modify the default file to look like this, we'll go through each part individually.

    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();
            }
        }
    }

    Firstly we need to tell the compiler that we are using Hazel; and using Hazel.Tcp; so we have access to Hazel's general and TCP specific types. Secondly we create a ConnectionListener, these wait on a specified port and accept new clients to the server invoking the NewConnection event each time a new client connects.

    We then instruct the ConnectionListener to begin listening for new clients by calling Start and then finally we close the listener using Close.

    In this circumstance it would be much better if we enclosed the listener within a using 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 using block then look at the unit tests.

    If you want to use UDP instead of TCP then it is as simple as including the Hazel.Udp namespace and creating a UdpConnectionListener instead.

Handling Events

  1. In the last example we subscribed to the NewConnection event but we never spsecified what to do in that event. Add the following method to your code.

    C#
    static void NewConnectionHandler(object sender, NewConnectionEventArgs args)
    {
        Console.WriteLine("New connection from " + args.Connection.EndPoint.ToString();
    
        args.Connection.DataReceived += DataReceivedHandler;
    }

    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.

    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.

    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.

  2. Once again we've got an undeclared event handler so lets fill that in now.

    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();
    }

    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 SendBytes. 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.

    You have also probably noticed that I didn't mention the Recycle 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!

In total, you should have something like this.

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();
        }
    }
}
Creating a Client

Connecting to a Server

  1. Create a new project in your solution for th client and add a reference to Hazel.dll.

  2. Modify the default file to contain the following.

    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();
            }
        }
    }

    As you can see you simply create a NetworkEndPoint for the remote server and then pass it into a new Connection. Then you can setup any events needed and finally call Connect to begin the connection.

    Finally we close the connection using Close. Again, Connection implements IDisposable and so must be closed, if it is easier you could wrap it in a using block.

    If you are using UDP instead of TCP then include the Hazel.Udp namespace and create a UdpClientConnection instead.

Handling Events

  • 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.

    C#
    private static void DataReceived(object sender, DataEventArgs args)
    {
        Console.WriteLine("Received (" + string.Join<byte>(", ", args.Bytes) + ") from " + connection.EndPoint.ToString());
    
        args.Recycle();
    }

Sending Messages

  • Sending a message is the same for both the server and client, you simply call SendBytes on your connection.

    Add the following line after the call to Connect

    connection.SendBytes(new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 });

You should have something like this.

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();
        }
    }
}