public static TStreamingHub Connect <TStreamingHub, TReceiver>(CallInvoker callInvoker, TReceiver receiver, string host = null, CallOptions option = default(CallOptions), MessagePackSerializerOptions serializerOptions = null, IMagicOnionClientLogger logger = null)
            where TStreamingHub : IStreamingHub <TStreamingHub, TReceiver>
        {
            var client = CreateClient <TStreamingHub, TReceiver>(callInvoker, receiver, host, option, serializerOptions, logger);

            async void ConnectAndForget()
            {
                var task = client.__ConnectAndSubscribeAsync(receiver, CancellationToken.None);

                try
                {
                    await task;
                }
                catch (Exception e)
                {
                    logger?.Error(e, "An error occurred while connecting to the server.");
                }
            }

            ConnectAndForget();

            return((TStreamingHub)(object)client);
        }
        async Task StartSubscribe()
        {
            var syncContext = SynchronizationContext.Current; // capture SynchronizationContext.
            var reader      = connection.RawStreamingCall.ResponseStream;

            try
            {
                while (await reader.MoveNext(cts.Token).ConfigureAwait(false)) // avoid Post to SyncContext(it losts one-frame per operation)
                {
                    try
                    {
                        // MessageFormat:
                        // broadcast: [methodId, [argument]]
                        // response:  [messageId, methodId, response]
                        // error-response: [messageId, statusCode, detail, StringMessage]
                        void ConsumeData(byte[] data)
                        {
                            var messagePackReader = new MessagePackReader(data);
                            var arrayLength       = messagePackReader.ReadArrayHeader();

                            if (arrayLength == 3)
                            {
                                var    messageId = messagePackReader.ReadInt32();
                                object future;
                                if (responseFutures.TryRemove(messageId, out future))
                                {
                                    var methodId = messagePackReader.ReadInt32();
                                    try
                                    {
                                        var offset = (int)messagePackReader.Consumed;
                                        var rest   = new ArraySegment <byte>(data, offset, data.Length - offset);
                                        OnResponseEvent(methodId, future, rest);
                                    }
                                    catch (Exception ex)
                                    {
                                        if (!(future as ITaskCompletion).TrySetException(ex))
                                        {
                                            throw;
                                        }
                                    }
                                }
                            }
                            else if (arrayLength == 4)
                            {
                                var    messageId = messagePackReader.ReadInt32();
                                object future;
                                if (responseFutures.TryRemove(messageId, out future))
                                {
                                    var statusCode = messagePackReader.ReadInt32();
                                    var detail     = messagePackReader.ReadString();
                                    var offset     = (int)messagePackReader.Consumed;
                                    var rest       = new ArraySegment <byte>(data, offset, data.Length - offset);
                                    var error      = MessagePackSerializer.Deserialize <string>(rest, serializerOptions);
                                    var ex         = default(RpcException);
                                    if (string.IsNullOrWhiteSpace(error))
                                    {
                                        ex = new RpcException(new Status((StatusCode)statusCode, detail));
                                    }
                                    else
                                    {
                                        ex = new RpcException(new Status((StatusCode)statusCode, detail), detail + Environment.NewLine + error);
                                    }

                                    (future as ITaskCompletion).TrySetException(ex);
                                }
                            }
                            else
                            {
                                var methodId = messagePackReader.ReadInt32();
                                var offset   = (int)messagePackReader.Consumed;
                                if (syncContext != null)
                                {
                                    var tuple = Tuple.Create(methodId, data, offset, data.Length - offset);
                                    syncContext.Post(state =>
                                    {
                                        var t = (Tuple <int, byte[], int, int>)state;
                                        OnBroadcastEvent(t.Item1, new ArraySegment <byte>(t.Item2, t.Item3, t.Item4));
                                    }, tuple);
                                }
                                else
                                {
                                    OnBroadcastEvent(methodId, new ArraySegment <byte>(data, offset, data.Length - offset));
                                }
                            }
                        }

                        ConsumeData(reader.Current);
                    }
                    catch (Exception ex)
                    {
                        const string msg = "Error on consume received message, but keep subscribe.";
                        // log post on main thread.
                        if (syncContext != null)
                        {
                            syncContext.Post(state => logger.Error((Exception)state, msg), ex);
                        }
                        else
                        {
                            logger.Error(ex, msg);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                if (ex is OperationCanceledException)
                {
                    return;
                }
                const string msg = "Error on subscribing message.";
                // log post on main thread.
                if (syncContext != null)
                {
                    syncContext.Post(state => logger.Error((Exception)state, msg), ex);
                }
                else
                {
                    logger.Error(ex, msg);
                }
            }
            finally
            {
                try
                {
                    // set syncContext before await
                    if (syncContext != null && SynchronizationContext.Current == null)
                    {
                        SynchronizationContext.SetSynchronizationContext(syncContext);
                    }
                    await DisposeAsyncCore(false);
                }
                finally
                {
                    waitForDisconnect.TrySetResult(null);
                }
            }
        }
        async Task StartSubscribe(SynchronizationContext syncContext, Task <bool> firstMoveNext)
        {
            var reader = connection.RawStreamingCall.ResponseStream;

            try
            {
                var moveNext = firstMoveNext;
                while (await moveNext.ConfigureAwait(false)) // avoid Post to SyncContext(it losts one-frame per operation)
                {
                    try
                    {
                        ConsumeData(syncContext, reader.Current);
                    }
                    catch (Exception ex)
                    {
                        const string msg = "An error occurred when consuming a received message, but the subscription is still alive.";
                        // log post on main thread.
                        if (syncContext != null)
                        {
                            syncContext.Post(state => logger.Error((Exception)state, msg), ex);
                        }
                        else
                        {
                            logger.Error(ex, msg);
                        }
                    }

                    moveNext = reader.MoveNext(cts.Token);
                }
            }
            catch (Exception ex)
            {
                if (ex is OperationCanceledException)
                {
                    return;
                }
                const string msg = "An error occurred while subscribing to messages.";
                // log post on main thread.
                if (syncContext != null)
                {
                    syncContext.Post(state => logger.Error((Exception)state, msg), ex);
                }
                else
                {
                    logger.Error(ex, msg);
                }
            }
            finally
            {
                try
                {
                    // set syncContext before await
                    if (syncContext != null && SynchronizationContext.Current == null)
                    {
                        SynchronizationContext.SetSynchronizationContext(syncContext);
                    }
                    await DisposeAsyncCore(false);
                }
                finally
                {
                    waitForDisconnect.TrySetResult(null);
                }
            }
        }