/// <summary>
        /// Called when this RpcSession is connected to the socket.
        /// </summary>
        protected override void OnConnected()
        {
            // If this is a new session on the server, send the welcome message.
            if (BaseSocket.Mode == SocketMode.Server)
            {
                Server.ServerInfo.RequireAuthentication = Config.RequireAuthentication;

                var serializer = SerializationCache.Get();

                serializer.MessageWriter.Write((byte)MqCommandType.RpcCommand);
                serializer.MessageWriter.Write((byte)RpcCommandType.WelcomeMessage);
                serializer.SerializeToWriter(Server.ServerInfo);

                var message = serializer.MessageWriter.ToMessage(true);

                message[0].FrameType = MqFrameType.Command;

                // RpcCommand:byte; RpcCommandType:byte; RpcServerInfoDataContract:byte[];
                Send(message);
            }

            base.OnConnected();

            // If the server does not require authentication, alert the server session that it is ready.
            if (BaseSocket.Mode == SocketMode.Server && Config.RequireAuthentication == false)
            {
                Authenticated = true;
                Ready?.Invoke(this, new SessionEventArgs <TSession, TConfig>((TSession)this));
            }
        }
        /// <summary>
        /// Called when this session is being setup.
        /// </summary>
        protected override void OnSetup()
        {
            base.OnSetup();

            // Determine if this session is running on the server or client to retrieve the worker thread pool.
            if (BaseSocket.Mode == SocketMode.Server)
            {
                Server = (RpcServer <TSession, TConfig>)BaseSocket;
            }
            else
            {
                Client = (RpcClient <TSession, TConfig>)BaseSocket;
            }

            SerializationCache = new SerializationCache(Config);

            RpcCallHandler = new RpcCallMessageHandler <TSession, TConfig>((TSession)this);
            MessageHandlers.Add(RpcCallHandler.Id, RpcCallHandler);
        }
        /// <summary>
        /// Processes an incoming command frame from the connection.
        /// Captures the call if it is a Rpc command.
        /// </summary>
        /// <param name="frame">Command frame to process.</param>
        protected override void ProcessCommand(MqFrame frame)
        {
            var commandType = (MqCommandType)frame.ReadByte(0);

            // If this is a base MqCommand, pass this directly on to the base command handler.
            if (commandType != MqCommandType.RpcCommand)
            {
                base.ProcessCommand(frame);
                return;
            }

            try
            {
                var rpcCommandType = (RpcCommandType)frame.ReadByte(1);

                if (rpcCommandType == RpcCommandType.WelcomeMessage)
                {
                    // RpcCommand:byte; RpcCommandType:byte; RpcServerInfoDataContract:byte[];

                    // Ensure that this command is running on the client.
                    if (BaseSocket.Mode != SocketMode.Client)
                    {
                        Close(SocketCloseReason.ProtocolError);
                        return;
                    }

                    var serializer = SerializationCache.Get(new MqMessage(frame));

                    // Forward the reader two bytes to the data.
                    serializer.MessageReader.ReadBytes(2);

                    serializer.PrepareDeserializeReader();

                    // Try to read the information from the server about the server.
                    Client.ServerInfo =
                        serializer.DeserializeFromReader(typeof(RpcServerInfoDataContract)) as RpcServerInfoDataContract;

                    if (Client.ServerInfo == null)
                    {
                        Close(SocketCloseReason.ProtocolError);
                        return;
                    }

                    // Check to see if the server requires authentication.  If so, send a auth check.
                    if (Client.ServerInfo.RequireAuthentication)
                    {
                        var authArgs = new RpcAuthenticateEventArgs <TSession, TConfig>((TSession)this);

                        // Start the authentication event to get the auth data.
                        Authenticate?.Invoke(this, authArgs);

                        serializer.MessageWriter.Write((byte)MqCommandType.RpcCommand);
                        serializer.MessageWriter.Write((byte)RpcCommandType.AuthenticationRequest);

                        if (authArgs.AuthData == null)
                        {
                            authArgs.AuthData = new byte[] { 0 };
                        }


                        serializer.MessageWriter.Write(authArgs.AuthData, 0, authArgs.AuthData.Length);

                        var authMessage = serializer.MessageWriter.ToMessage(true);
                        authMessage[0].FrameType = MqFrameType.Command;

                        _authTimeout = new Task(async() =>
                        {
                            try
                            {
                                await Task.Delay(Config.ConnectionTimeout, _authTimeoutCancel.Token);
                            }
                            catch
                            {
                                return;
                            }

                            if (!_authTimeoutCancel.IsCancellationRequested)
                            {
                                Close(SocketCloseReason.TimeOut);
                            }
                        });

                        // RpcCommand:byte; RpcCommandType:byte; AuthData:byte[];
                        Send(authMessage);

                        _authTimeout.Start();
                    }
                    else
                    {
                        // If no authentication is required, set this client to authenticated.
                        Authenticated = true;

                        // Alert the server that this session is ready for usage.
                        Task.Run(
                            () => { Ready?.Invoke(this, new SessionEventArgs <TSession, TConfig>((TSession)this)); });
                    }

                    SerializationCache.Put(serializer);
                }
                else if (rpcCommandType == RpcCommandType.AuthenticationRequest)
                {
                    // RpcCommand:byte; RpcCommandType:byte; AuthData:byte[];

                    // If this is not run on the server, quit.
                    if (BaseSocket.Mode != SocketMode.Server)
                    {
                        Close(SocketCloseReason.ProtocolError);
                        return;
                    }

                    // Ensure that the server requires authentication.
                    if (Server.Config.RequireAuthentication == false)
                    {
                        Close(SocketCloseReason.ProtocolError);
                        return;
                    }

                    byte[] authBytes = new byte[frame.DataLength - 2];
                    frame.Read(2, authBytes, 0, authBytes.Length);

                    var authArgs = new RpcAuthenticateEventArgs <TSession, TConfig>((TSession)this)
                    {
                        AuthData = authBytes
                    };


                    Authenticate?.Invoke(this, authArgs);

                    Authenticated = authArgs.Authenticated;

                    if (Authenticated == false)
                    {
                        Close(SocketCloseReason.AuthenticationFailure);
                    }
                    else
                    {
                        var authFrame = CreateFrame(new byte[authArgs.AuthData.Length + 2], MqFrameType.Command);
                        authFrame.Write(0, (byte)MqCommandType.RpcCommand);
                        authFrame.Write(1, (byte)RpcCommandType.AuthenticationResult);

                        // State of the authentication
                        authFrame.Write(2, Authenticated);

                        // RpcCommand:byte; RpcCommandType:byte; AuthResult:bool;
                        Send(authFrame);

                        // Alert the server that this session is ready for usage.
                        Task.Run(
                            () => { Ready?.Invoke(this, new SessionEventArgs <TSession, TConfig>((TSession)this)); });
                    }
                }
                else if (rpcCommandType == RpcCommandType.AuthenticationResult)
                {
                    // RpcCommand:byte; RpcCommandType:byte; AuthResult:bool;

                    // Cancel the timeout request.
                    _authTimeoutCancel.Cancel();

                    // Ensure that this command is running on the client.
                    if (BaseSocket.Mode != SocketMode.Client)
                    {
                        Close(SocketCloseReason.ProtocolError);
                        return;
                    }

                    if (Client.Config.RequireAuthentication == false)
                    {
                        Close(SocketCloseReason.ProtocolError);
                        return;
                    }

                    Authenticated = true;

                    var authArgs = new RpcAuthenticateEventArgs <TSession, TConfig>((TSession)this)
                    {
                        Authenticated = frame.ReadBoolean(2)
                    };

                    // Alert the client that the sesion has been authenticated.
                    AuthenticationSuccess?.Invoke(this, authArgs);

                    // Alert the client that this session is ready for usage.
                    Task.Run(() => { Ready?.Invoke(this, new SessionEventArgs <TSession, TConfig>((TSession)this)); });
                }
                else
                {
                    Close(SocketCloseReason.ProtocolError);
                }
            }
            catch (Exception)
            {
                Close(SocketCloseReason.ProtocolError);
            }
        }