/// <summary>
        /// 自动重连
        /// </summary>
        /// <returns></returns>
        public Task AutoReconnect()
        {
            var reconnectingArgs = new AVIMReconnectingEventArgs()
            {
                ClientId     = _clientId,
                IsAuto       = true,
                SessionToken = _sesstionToken
            };

            m_OnReconnecting?.Invoke(this, reconnectingArgs);

            return(OpenAsync(_wss, Subprotocol).ContinueWith(t =>
            {
                if (t.IsFaulted || t.Exception != null)
                {
                    state = Status.Reconnecting;
                    var reconnectFailedArgs = new AVIMReconnectFailedArgs()
                    {
                        ClientId = _clientId,
                        IsAuto = true,
                        SessionToken = _sesstionToken,
                        FailedCode = 0  // network broken.
                    };
                    m_OnReconnectFailed?.Invoke(this, reconnectFailedArgs);
                    state = Status.Offline;
                    return Task.FromResult(false);
                }
                else
                {
                    if (t.Result)
                    {
                        state = Status.Opened;

                        if (this.IsSesstionTokenExpired)
                        {
                            AVRealtime.PrintLog("sesstion is expired, auto relogin with clientId :" + _clientId);
                            return this.LogInAsync(_clientId, this._tag, this._deviceId, this._secure).OnSuccess(o =>
                            {
                                ClearReconnectTimer();
                                return true;
                            });
                        }
                        else
                        {
                            var sessionCMD = new SessionCommand().UA(VersionString).R(1);

                            if (string.IsNullOrEmpty(_tag))
                            {
                                sessionCMD = sessionCMD.Tag(_tag).SessionToken(this._sesstionToken);
                            }

                            var cmd = sessionCMD.Option("open")
                                      .PeerId(_clientId);

                            AVRealtime.PrintLog("reopen sesstion with sesstion token :" + _sesstionToken);
                            return AVIMCommandRunner.RunCommandAsync(cmd).OnSuccess(c =>
                            {
                                ClearReconnectTimer();
                                return true;
                            });
                        }
                    }
                    else
                    {
                        return Task.FromResult(false);
                    }
                }
            }).Unwrap().ContinueWith(s =>
            {
                if (s.IsFaulted || s.Exception != null)
                {
                    var reconnectFailedArgs = new AVIMReconnectFailedArgs()
                    {
                        ClientId = _clientId,
                        IsAuto = true,
                        SessionToken = _sesstionToken,
                        FailedCode = 1
                    };
                    m_OnReconnectFailed?.Invoke(this, reconnectFailedArgs);
                    state = Status.Offline;
                }
                else
                {
                    if (s.Result)
                    {
                        autoReconnectionStarted = false;
                        reconnectTimer = null;
                        var reconnectedArgs = new AVIMReconnectedEventArgs()
                        {
                            ClientId = _clientId,
                            IsAuto = true,
                            SessionToken = _sesstionToken,
                        };
                        state = Status.Online;
                        m_OnReconnected?.Invoke(this, reconnectedArgs);
                    }
                }
            }));
        }
        /// <summary>
        /// 自动重连
        /// </summary>
        /// <returns></returns>
        Task AutoReconnect()
        {
            AVRealtime.PrintLog("AutoReconnect started.");
            var reconnectingArgs = new AVIMReconnectingEventArgs()
            {
                ClientId     = _clientId,
                IsAuto       = true,
                SessionToken = _sesstionToken
            };

            m_OnReconnecting?.Invoke(this, reconnectingArgs);

            var         tcs = new TaskCompletionSource <bool>();
            Task <bool> task;

            if (reborn)
            {
                AVRealtime.PrintLog("both preferred and secondary websockets are expired, so try to request RTM router to get a new pair");
                task = OpenAsync(this._secure, Subprotocol, true);
            }
            else
            {
                var websocketServer = _wss;
                if (useSecondary)
                {
                    AVRealtime.PrintLog(string.Format("preferred websocket server ({0}) network broken, take secondary server({1}) :", _wss, _secondaryWss));
                    websocketServer = _secondaryWss;
                }
                task = OpenAsync(websocketServer, Subprotocol, true);
            }

            task.ContinueWith(t =>
            {
                if (t.IsFaulted || t.IsCanceled)
                {
                    state = Status.Reconnecting;
                    var reconnectFailedArgs = new AVIMReconnectFailedArgs()
                    {
                        ClientId     = _clientId,
                        IsAuto       = true,
                        SessionToken = _sesstionToken,
                        FailedCode   = 0// network broken.
                    };
                    m_OnReconnectFailed?.Invoke(this, reconnectFailedArgs);
                    state = Status.Offline;
                    tcs.SetException(t.Exception);
                    throw t.Exception;
                }
                else
                {
                    state = Status.Opened;
                    SetNetworkState();

                    void onClose(int code, string reason, string detail)
                    {
                        AVRealtime.PrintLog("disconnect when open session");
                        var ex = new Exception("connection is closed");
                        tcs.SetException(ex);
                        AVWebSocketClient.OnClosed -= onClose;
                        throw ex;
                    };
                    AVWebSocketClient.OnClosed += onClose;

                    if (this.IsSesstionTokenExpired)
                    {
                        AVRealtime.PrintLog("session is expired, auto relogin with clientId :" + _clientId);
                        return(this.LogInAsync(_clientId, this._tag, this._deviceId, this._secure).ContinueWith(o => {
                            AVWebSocketClient.OnClosed -= onClose;
                            return !o.IsFaulted;
                        }));
                    }
                    else
                    {
                        var sessionCMD = new SessionCommand().UA(VersionString).R(1);

                        if (string.IsNullOrEmpty(_tag))
                        {
                            sessionCMD = sessionCMD.Tag(_tag).SessionToken(this._sesstionToken);
                        }

                        var cmd = sessionCMD.Option("open")
                                  .PeerId(_clientId);

                        AVRealtime.PrintLog("reopen session with session token :" + _sesstionToken);
                        return(RunCommandAsync(cmd).ContinueWith(o => {
                            AVWebSocketClient.OnClosed -= onClose;
                            return !o.IsFaulted;
                        }));
                    }
                }
            }).Unwrap().ContinueWith(s =>
            {
                if (s.IsFaulted || s.Exception != null)
                {
                    var reconnectFailedArgs = new AVIMReconnectFailedArgs()
                    {
                        ClientId     = _clientId,
                        IsAuto       = true,
                        SessionToken = _sesstionToken,
                        FailedCode   = 1
                    };
                    m_OnReconnectFailed?.Invoke(this, reconnectFailedArgs);
                    state = Status.Offline;
                    tcs.SetException(s.Exception);
                }
                else
                {
                    var reconnectedArgs = new AVIMReconnectedEventArgs()
                    {
                        ClientId     = _clientId,
                        IsAuto       = true,
                        SessionToken = _sesstionToken,
                    };
                    state = Status.Online;
                    m_OnReconnected?.Invoke(this, reconnectedArgs);
                    ToggleNotification(true);
                    ToggleHeartBeating(true);
                    tcs.SetResult(true);
                }
            });

            return(tcs.Task);
        }
        /// <summary>
        /// 自动重连
        /// </summary>
        /// <returns></returns>
        public Task AutoReconnect()
        {
            AVRealtime.PrintLog("AutoReconnect started.");
            var reconnectingArgs = new AVIMReconnectingEventArgs()
            {
                ClientId     = _clientId,
                IsAuto       = true,
                SessionToken = _sesstionToken
            };

            m_OnReconnecting?.Invoke(this, reconnectingArgs);

            var websocketServer = _wss;

            if (useSecondary)
            {
                websocketServer = _secondaryWss;
            }

            Task <bool> task;

            if (reborn)
            {
                AVRealtime.PrintLog("both preferred and secondary websockets are expired, so try to request RTM router to get a new pair");
                task = OpenAsync(this._secure, Subprotocol, true);
            }
            else
            {
                AVRealtime.PrintLog(string.Format("preferred websocket server ({0}) network broken, take secondary server({1}) :", _wss, _secondaryWss));
                task = OpenAsync(websocketServer, Subprotocol, true);
            }

            return(task.ContinueWith(t =>
            {
                if (!t.Result)
                {
                    state = Status.Reconnecting;
                    var reconnectFailedArgs = new AVIMReconnectFailedArgs()
                    {
                        ClientId = _clientId,
                        IsAuto = true,
                        SessionToken = _sesstionToken,
                        FailedCode = 0  // network broken.
                    };
                    m_OnReconnectFailed?.Invoke(this, reconnectFailedArgs);
                    state = Status.Offline;
                    return Task.FromResult(false);
                }
                else
                {
                    state = Status.Opened;
                    SetNetworkState();
                    if (this.IsSesstionTokenExpired)
                    {
                        AVRealtime.PrintLog("sesstion is expired, auto relogin with clientId :" + _clientId);
                        return this.LogInAsync(_clientId, this._tag, this._deviceId, this._secure).OnSuccess(o =>
                        {
                            ClearReconnectTimer();
                            return true;
                        });
                    }
                    else
                    {
                        var sessionCMD = new SessionCommand().UA(VersionString).R(1);

                        if (string.IsNullOrEmpty(_tag))
                        {
                            sessionCMD = sessionCMD.Tag(_tag).SessionToken(this._sesstionToken);
                        }

                        var cmd = sessionCMD.Option("open")
                                  .PeerId(_clientId);

                        AVRealtime.PrintLog("reopen sesstion with sesstion token :" + _sesstionToken);
                        return RunCommandAsync(cmd).OnSuccess(c =>
                        {
                            ClearReconnectTimer();
                            return true;
                        });
                    }
                }
            }).Unwrap().ContinueWith(s =>
            {
                if (s.IsFaulted || s.Exception != null)
                {
                    var reconnectFailedArgs = new AVIMReconnectFailedArgs()
                    {
                        ClientId = _clientId,
                        IsAuto = true,
                        SessionToken = _sesstionToken,
                        FailedCode = 1
                    };
                    m_OnReconnectFailed?.Invoke(this, reconnectFailedArgs);
                    state = Status.Offline;
                    autoReconnectionStarted = false;
                }
                else
                {
                    if (s.Result)
                    {
                        reconnectTimer = null;
                        var reconnectedArgs = new AVIMReconnectedEventArgs()
                        {
                            ClientId = _clientId,
                            IsAuto = true,
                            SessionToken = _sesstionToken,
                        };
                        state = Status.Online;
                        autoReconnectionStarted = false;
                        m_OnReconnected?.Invoke(this, reconnectedArgs);
                    }
                }
            }));
        }