/// <summary>
        /// Asynchronous task for handling the connection (connection, data receive loop, disconnect, error handling).
        /// </summary>
        /// <param name="hostname"></param>
        /// <param name="port"></param>
        /// <returns></returns>
        private async Task ClientTask(string hostname, int port)
        {
            try
            {
                await _tcpClient.ConnectAsync(hostname, port);

                if (OnConnect != null)
                    OnConnect.Invoke(this);

                using (NetworkStream netStream = _tcpClient.GetStream())
                {
                    var framer = new LengthPrefixPacketFramer(_bufferSize);

                    while (!_cancellation.Token.IsCancellationRequested)
                    {
                        var buffer = new byte[_bufferSize];
                        var bytesRead = await netStream.ReadAsync(buffer, 0, buffer.Length);

                        bool messageReceived = framer.DataReceived(buffer);
                        if (messageReceived)
                        {
                            if (OnMessageReceived != null)
                                OnMessageReceived.Invoke(this, new SACMessageReceivedEventArgs(framer.GetMessage(), framer.GetMessage().Length));
                        }
                    }
                }
            }
            catch (Exception exception)
            {
                if (OnError != null)
                    OnError.Invoke(this, new SACErrorEventArgs(exception));
            }
            finally
            {
                if (OnDisconnect != null)
                    OnDisconnect.Invoke(this);
                
                _tcpClient.Close();
                _tcpClient = null;
                _cancellation.Dispose();
                _cancellation = new CancellationTokenSource();
            }
        }
 /// <summary>
 /// Sends data to the remote host and automatically frames the message.
 /// </summary>
 /// <param name="message"></param>
 public void Send(byte[] message)
 {
     var framer = new LengthPrefixPacketFramer(_bufferSize);
     var framedMessage = framer.Frame(message);
     SendAsync(framedMessage).FireAndForget();
 }
        /// <summary>
        /// This method asynchronously handles one client.
        /// It is responsible for reading data from the client and invokes events as they occur.
        /// Automatically unframes framed messages.
        /// </summary>
        /// <param name="tcpClient"></param>
        /// <returns></returns>
        private async Task HandleTcpClientAsync(TcpClient tcpClient)
        {
            try
            {
                if (OnClientConnected != null)
                    OnClientConnected.Invoke(this, new SASClientConnectedEventArgs(tcpClient));

                using (NetworkStream netStream = tcpClient.GetStream())
                {
                    var framer = new LengthPrefixPacketFramer(_bufferSize);

                    while (tcpClient.Connected && !_cancellation.Token.IsCancellationRequested)
                    {
                        var buffer = new byte[_bufferSize];
                        var bytesRead = await netStream.ReadAsync(buffer, 0, buffer.Length);

                        bool messageReceived = framer.DataReceived(buffer);
                        if (messageReceived)
                        {
                            if (OnClientMessageReceived != null)
                                OnClientMessageReceived.Invoke(this, new SASClientMessageReceivedEventArgs(tcpClient, framer.GetMessage(), framer.GetMessage().Length));
                        }
                    }
                }
            }
            catch (Exception exception)
            {
                if (OnClientError != null)
                    OnClientError.Invoke(this, new SASClientErrorEventArgs(tcpClient, exception));
            }
            finally
            {
                if (OnClientDisconnected != null)
                    OnClientDisconnected.Invoke(this, new SASClientDisconnectedEventArgs(tcpClient));

                tcpClient.Close();
            }
        }