/// <summary>
        /// Begins a receive operation on <see cref="Socket"/> using the <typeparamref name="T"/> message handler implementation
        /// </summary>
        public async Task BeginReceiveAsync <T>(CancellationToken?cancellationToken = null) where T : NetworkMessageHandler
        {
            try {
                if (SerializationHandler == null)
                {
                    // SerializationHandler must be implemented to transform the network message from binary data to T
                    throw new ArgumentNullException(nameof(SerializationHandler));
                }

                var messageHandler = Activator.CreateInstance <T>();
                while (true)
                {
                    // try receive data from connected socket
                    int bytesTransferred;
                    try {
                        bytesTransferred = await _stream.ReadAsync(messageHandler.Buffer, messageHandler.Offset, messageHandler.Length, cancellationToken ?? _cancellationSource.Token)
                                           .ConfigureAwait(false); // to avoid deadlock, continue the execution on any free thread at the time
                    } catch (Exception ex) {
                        if (ex.InnerException is SocketException)
                        {
                            var socketEx = ex.InnerException as SocketException;
                            if (socketEx.SocketErrorCode == SocketError.ConnectionReset ||
                                socketEx.SocketErrorCode == SocketError.ConnectionAborted)
                            {
                                // 'ConnectionReset' will happen when the other party has closed the connection unexpectedly
                                ClientHandler?.OnConnectionClosedAsync(this);
                                break;
                            }
                        }

                        // unhandled
                        throw;
                    }

                    if (bytesTransferred == 0)
                    {
                        // zero bytes means the connection has been closed
                        await ClientHandler?.OnConnectionClosedAsync(this);

                        break;
                    }

                    // process the received data in the message handler
                    var isReceiveCompleted = messageHandler.CompleteReceive(bytesTransferred);
                    if (isReceiveCompleted == null)
                    {
                        // invoke the client handler to process the error in the implementation
                        ClientHandler?.OnReceiveError(this, new SocketException((int)SocketError.MessageSize));
                        continue;
                    }

                    if ((bool)isReceiveCompleted)
                    {
                        // finalize (deobfuscation etc), deserialize to complete message, and process in protocol
                        await ClientHandler?.OnMessageAsync(this, SerializationHandler.Deserialize(messageHandler.GetFinalized()));

                        // message has been processed, so handler can be resetted
                        messageHandler.Reset();
                    }
                }
            } catch (Exception ex) {
                ClientHandler?.OnReceiveError(this, ex);
            }
        }