/// <summary>
        /// Attempt to connect to the server via the provided hostname and port.
        /// </summary>
        /// <param name="callbackFunction"> a function to be called when a connection is made </param>
        /// <param name="hostname"> name of the server to connect to </param>
        /// <returns></returns>
        public static Socket ConnectToServer(NetworkAction callbackFunction, string hostname, int port)
        {
            // Create a TCP/IP socket.
            MakeSocket(hostname, out Socket socket, out IPAddress ipAddress);

            // make a new state of the socket we just made
            SocketState state = new SocketState(socket, -1);

            // call the correct function needed for the client
            state.SetNetworkAction(callbackFunction);

            // make a new connection to the server
            state.GetSocket().BeginConnect(ipAddress, port, ConnectedCallback, state);

            // return the current socket with respect to the state
            return(state.GetSocket());
        }
        /// <summary>
        /// This function is "called" by the operating system when a remote
        /// connection request comes in.
        ///
        /// AcceptNewClient creates a new socket and saves it in a SocketState
        /// object then invokes the NetworkAction delegate stored by the
        /// parameter ar and continues the event loop.
        /// </summary>
        public static void AcceptNewClient(IAsyncResult ar)
        {
            // extract the ConnectionState from ar
            ConnectionState cs = (ConnectionState)ar.AsyncState;

            // extract the socket from the TCP listener
            Socket socket = cs.GetTcpListener().EndAcceptSocket(ar);

            // save the socket along with a network action
            SocketState ss = new SocketState(socket, -1);

            ss.SetNetworkAction(cs.GetNetworkAction());

            // invoke the network action and pass it our new socket state
            ss.InvokeNetworkAction(ss);

            //continue the event loop
            cs.GetTcpListener().BeginAcceptSocket(AcceptNewClient, cs);
        }