/// <summary>
        /// Open websocket connection.
        /// </summary>
        /// <returns>The async.</returns>
        /// <param name="secure">If set to <c>true</c> secure.</param>
        /// <param name="subprotocol">Subprotocol.</param>
        /// <param name="enforce">If set to <c>true</c> enforce.</param>
        /// <param name="cancellationToken">Cancellation token.</param>
        public Task <bool> OpenAsync(bool secure, string subprotocol = null, bool enforce = false, CancellationToken cancellationToken = default(CancellationToken))
        {
            _secure = secure;
            if (state == Status.Online && !enforce)
            {
                AVRealtime.PrintLog("state is Status.Online.");
                return(Task.FromResult(true));
            }

            if (CurrentConfiguration.RealtimeServer != null)
            {
                _wss = CurrentConfiguration.RealtimeServer.ToString();
                AVRealtime.PrintLog("use configuration realtime server with url: " + _wss);
                return(OpenAsync(_wss, subprotocol, enforce));
            }
            var routerUrl = CurrentConfiguration.RTMRouter != null?CurrentConfiguration.RTMRouter.ToString() :
                                (AVClient.CurrentConfiguration.PushServer != null ? AVClient.CurrentConfiguration.PushServer.ToString() : null);

            return(RouterController.GetAsync(routerUrl, secure, cancellationToken).OnSuccess(r =>
            {
                var routerState = r.Result;
                if (routerState == null)
                {
                    return Task.FromResult(false);
                }
                _wss = routerState.server;
                _secondaryWss = routerState.secondary;
                state = Status.Connecting;
                AVRealtime.PrintLog("push router give a url :" + _wss);
                return OpenAsync(routerState.server, subprotocol, enforce);
            }).Unwrap());
        }
        /// <summary>
        /// 创建 Client
        /// </summary>
        /// <param name="clientId"></param>
        /// <param name="tag"></param>
        /// <param name="deviceId">设备唯一的 Id。如果是 iOS 设备,需要将 iOS 推送使用的 DeviceToken 作为 deviceId 传入</param>
        /// <param name="secure">是否强制加密 wss 链接</param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public Task <AVIMClient> CreateClientAsync(string clientId,
                                                   string tag      = null,
                                                   string deviceId = null,
                                                   bool secure     = true,
                                                   CancellationToken cancellationToken = default(CancellationToken))
        {
            lock (mutex)
            {
                var client = PreLogIn(clientId, tag, deviceId);

                AVRealtime.PrintLog("begin OpenAsync.");
                return(OpenAsync(secure, Subprotocol, true, cancellationToken).OnSuccess(t =>
                {
                    if (!t.Result)
                    {
                        return Task.FromResult <AVIMCommand>(null);
                    }
                    AVRealtime.PrintLog("websocket server connected, begin to open sesstion.");
                    SetNetworkState();
                    var cmd = new SessionCommand()
                              .UA(VersionString)
                              .Tag(tag)
                              .DeviceId(deviceId)
                              .Option("open")
                              .PeerId(clientId);

                    ToggleNotification(true);
                    return AttachSignature(cmd, this.SignatureFactory.CreateConnectSignature(clientId));
                }).Unwrap().OnSuccess(x =>
                {
                    var cmd = x.Result;
                    if (cmd == null)
                    {
                        return Task.FromResult <Tuple <int, IDictionary <string, object> > >(null);
                    }
                    return this.RunCommandAsync(cmd);
                }).Unwrap().OnSuccess(s =>
                {
                    if (s.Result == null)
                    {
                        return null;
                    }
                    AVRealtime.PrintLog("sesstion opened.");
                    state = Status.Online;
                    ToggleHeartBeating(true);
                    var response = s.Result.Item2;
                    if (response.ContainsKey("st"))
                    {
                        _sesstionToken = response["st"] as string;
                    }
                    if (response.ContainsKey("stTtl"))
                    {
                        var stTtl = long.Parse(response["stTtl"].ToString());
                        _sesstionTokenExpire = DateTime.Now.ToUnixTimeStamp() + stTtl * 1000;
                    }
                    AfterLogIn(client);
                    return client;
                }));
            }
        }
        /// <summary>
        /// 自动重连
        /// </summary>
        /// <returns></returns>
        public Task AutoReconnect()
        {
            return(OpenAsync(_wss).ContinueWith(t =>
            {
                state = Status.Reconnecting;
                var cmd = new SessionCommand()
                          .UA(VersionString)
                          .Tag(_tag)
                          .R(1)
                          .SessionToken(this._sesstionToken)
                          .Option("open")
                          .PeerId(_clientId);

                return AttachSignature(cmd, this.SignatureFactory.CreateConnectSignature(_clientId)).OnSuccess(_ =>
                {
                    return AVIMCommandRunner.RunCommandAsync(cmd);
                }).Unwrap();
            }).Unwrap().OnSuccess(s =>
            {
                var result = s.Result;
                if (result.Item1 == 0)
                {
                    state = Status.Online;
                }
                else
                {
                    state = Status.Offline;
                }
            }));
        }
        private void ReconnectTimer_Elapsed(object sender, TimerEventArgs e)
        {
            var _timer = sender as AVTimer;

            if (state == Status.Offline)
            {
                if (_timer != null)
                {
                    if (_timer.Executed <= this.ReconnectOptions.Retry && CanReconnect)
                    {
                        AutoReconnect();
                        PrintLog(string.Format("reconnect for {0} times,", _timer.Executed));
                        _timer.Executed += 1;
                        if (_timer.Executed == 3)
                        {
                            useSecondary = true;
                        }
                        if (_timer.Executed == 6)
                        {
                            reborn = true;
                        }
                    }
                    else
                    {
                        _timer.Stop();
                        _timer = null;
                        autoReconnectionStarted = false;

                        var reconnectFailedArgs = new AVIMReconnectFailedArgs()
                        {
                            ClientId     = _clientId,
                            IsAuto       = true,
                            SessionToken = _sesstionToken,
                            FailedCode   = -1
                        };
                        m_OnReconnectFailed?.Invoke(this, reconnectFailedArgs);
                        state = Status.Offline;
                    }
                }
                else
                {
                    if (CanReconnect)
                    {
                        AutoReconnect();
                    }
                }
            }
            else if (state == Status.Online)
            {
                if (_timer != null)
                {
                    _timer.Stop();
                    _timer = null;
                }
            }
        }
        /// <summary>
        /// 创建 Client
        /// </summary>
        /// <param name="clientId"></param>
        /// <param name="tag"></param>
        /// <param name="deviceId">设备唯一的 Id。如果是 iOS 设备,需要将 iOS 推送使用的 DeviceToken 作为 deviceId 传入</param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public Task <AVIMClient> CreateClient(
            string clientId,
            string tag      = null,
            string deviceId = null,
            CancellationToken cancellationToken = default(CancellationToken))
        {
            _clientId = clientId;
            _tag      = tag;
            if (_tag != null)
            {
                if (deviceId == null)
                {
                    throw new ArgumentNullException(deviceId, "当 tag 不为空时,必须传入当前设备不变的唯一 id(deviceId)");
                }
            }

            if (string.IsNullOrEmpty(clientId))
            {
                throw new Exception("当前 ClientId 为空,无法登录服务器。");
            }
            return(OpenAsync(cancellationToken).OnSuccess(t =>
            {
                ToggleNotification(true);

                var cmd = new SessionCommand()
                          .UA(VersionString)
                          .Tag(tag)
                          .Argument("deviceId", deviceId)
                          .Option("open")
                          .PeerId(clientId);

                return AttachSignature(cmd, this.SignatureFactory.CreateConnectSignature(clientId)).OnSuccess(_ =>
                {
                    return AVIMCommandRunner.RunCommandAsync(cmd);
                }).Unwrap();
            }).Unwrap().OnSuccess(s =>
            {
                if (s.Exception != null)
                {
                    var imException = s.Exception.InnerException as AVIMException;
                }
                state = Status.Online;
                var response = s.Result.Item2;
                if (response.ContainsKey("st"))
                {
                    _sesstionToken = response["st"] as string;
                }
                if (response.ContainsKey("stTtl"))
                {
                    var stTtl = long.Parse(response["stTtl"].ToString());
                    _sesstionTokenExpire = DateTime.Now.UnixTimeStampSeconds() + stTtl;
                }
                var client = new AVIMClient(clientId, tag, this);
                return client;
            }));
        }
 /// <summary>
 /// Opens WebSocket connection with LeanCloud Push server.
 /// </summary>
 /// <returns>The async.</returns>
 /// <param name="cancellationToken">Cancellation token.</param>
 public Task OpenAsync(CancellationToken cancellationToken = default(CancellationToken))
 {
     if (state == Status.Online)
     {
         return(Task.FromResult(true));
     }
     return(RouterController.GetAsync(cancellationToken).OnSuccess(_ =>
     {
         _wss = _.Result.server;
         state = Status.Connecting;
         return OpenAsync(_.Result.server, cancellationToken);
     }).Unwrap());
 }
        private void WebsocketClient_OnClosed(int errorCode, string reason, string detail)
        {
            PrintLog(string.Format("websocket closed with code is {0},reason is {1} and detail is {2}", errorCode, reason, detail));
            state = Status.Offline;

            var disconnectEventArgs = new AVIMDisconnectEventArgs(errorCode, reason, detail);

            m_OnDisconnected?.Invoke(this, disconnectEventArgs);

            this.WebSocketState = new WebSocketStateOptions()
            {
                ClosedCode = errorCode
            };
        }
        private void WebsocketClient_OnClosed(int arg1, string arg2, string arg3)
        {
            if (State != Status.Closed)
            {
                state = Status.Offline;
                PrintLog(string.Format("websocket closed with code is {0},reason is {1} and detail is {2}", arg1, arg2, arg3));
                var args = new AVIMDisconnectEventArgs(arg1, arg2, arg3);
                m_OnDisconnected?.Invoke(this, args);

                // 如果断线产生的原因是客户端掉线而不是服务端踢下线,则应该开始自动重连
                if (arg1 == 0)
                {
                    StartAutoReconnect();
                }
            }
        }
        /// <summary>
        /// 创建 Client
        /// </summary>
        /// <param name="clientId"></param>
        /// <param name="tag"></param>
        /// <param name="deviceId">设备唯一的 Id。如果是 iOS 设备,需要将 iOS 推送使用的 DeviceToken 作为 deviceId 传入</param>
        /// <param name="secure">是否强制加密 wss 链接</param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public Task <AVIMClient> CreateClientAsync(string clientId,
                                                   string tag      = null,
                                                   string deviceId = null,
                                                   bool secure     = true,
                                                   CancellationToken cancellationToken = default(CancellationToken))
        {
            lock (mutex)
            {
                var client = PreLogIn(clientId, tag, deviceId);

                AVRealtime.PrintLog("begin OpenAsync.");
                return(OpenAsync(secure, Subprotocol, cancellationToken).OnSuccess(t =>
                {
                    AVRealtime.PrintLog("OpenAsync OnSuccess. begin send open sesstion cmd.");

                    var cmd = new SessionCommand()
                              .UA(VersionString)
                              .Tag(tag)
                              .DeviceId(deviceId)
                              .Option("open")
                              .PeerId(clientId);

                    ToggleNotification(true);
                    return AttachSignature(cmd, this.SignatureFactory.CreateConnectSignature(clientId));
                }).Unwrap().OnSuccess(x =>
                {
                    var cmd = x.Result;
                    return AVIMCommandRunner.RunCommandAsync(cmd);
                }).Unwrap().OnSuccess(s =>
                {
                    AVRealtime.PrintLog("sesstion opened.");
                    state = Status.Online;
                    ToggleHeartBeating(_heartBeatingToggle);
                    var response = s.Result.Item2;
                    if (response.ContainsKey("st"))
                    {
                        _sesstionToken = response["st"] as string;
                    }
                    if (response.ContainsKey("stTtl"))
                    {
                        var stTtl = long.Parse(response["stTtl"].ToString());
                        _sesstionTokenExpire = DateTime.Now.UnixTimeStampSeconds() + stTtl;
                    }
                    return client;
                }));
            }
        }
        internal Task LogInAsync(string clientId,
                                 string tag      = null,
                                 string deviceId = null,
                                 bool secure     = true,
                                 CancellationToken cancellationToken = default(CancellationToken))
        {
            lock (mutex)
            {
                var cmd = new SessionCommand()
                          .UA(VersionString)
                          .Tag(tag)
                          .DeviceId(deviceId)
                          .Option("open")
                          .PeerId(clientId);

                var result = AttachSignature(cmd, this.SignatureFactory.CreateConnectSignature(clientId)).OnSuccess(_ =>
                {
                    return(RunCommandAsync(cmd));
                }).Unwrap().OnSuccess(t =>
                {
                    AVRealtime.PrintLog("sesstion opened.");
                    if (t.Exception != null)
                    {
                        var imException = t.Exception.InnerException as AVIMException;
                        throw imException;
                    }
                    state        = Status.Online;
                    var response = t.Result.Item2;
                    if (response.ContainsKey("st"))
                    {
                        _sesstionToken = response["st"] as string;
                    }
                    if (response.ContainsKey("stTtl"))
                    {
                        var stTtl            = long.Parse(response["stTtl"].ToString());
                        _sesstionTokenExpire = DateTime.Now.ToUnixTimeStamp() + stTtl * 1000;
                    }
                    return(t.Result);
                });

                return(result);
            }
        }
        internal Task OpenSessionAsync(string clientId,
                                       string tag       = null,
                                       string deviceId  = null,
                                       string nonce     = null,
                                       long timestamp   = 0,
                                       string signature = null,
                                       bool secure      = true)
        {
            var cmd = new SessionCommand()
                      .UA(VersionString)
                      .Tag(tag)
                      .DeviceId(deviceId)
                      .Option("open")
                      .PeerId(clientId)
                      .Argument("n", nonce)
                      .Argument("t", timestamp)
                      .Argument("s", signature);

            return(RunCommandAsync(cmd).OnSuccess(t =>
            {
                AVRealtime.PrintLog("sesstion opened.");
                if (t.Exception != null)
                {
                    var imException = t.Exception.InnerException as AVIMException;
                    throw imException;
                }
                state = Status.Online;
                var response = t.Result.Item2;
                if (response.ContainsKey("st"))
                {
                    _sesstionToken = response["st"] as string;
                }
                if (response.ContainsKey("stTtl"))
                {
                    var stTtl = long.Parse(response["stTtl"].ToString());
                    _sesstionTokenExpire = DateTime.Now.ToUnixTimeStamp() + stTtl * 1000;
                }
                return t.Result;
            }));
        }
        /// <summary>
        /// fetch wss address from push router and open the websocket connection.
        /// </summary>
        /// <param name="secure">if use ssl encrept</param>
        /// <param name="subprotocol">subprotocol</param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public Task OpenAsync(bool secure = true, string subprotocol = null, CancellationToken cancellationToken = default(CancellationToken))
        {
            if (state == Status.Online)
            {
                AVRealtime.PrintLog("state is Status.Online.");
                return(Task.FromResult(true));
            }

            if (CurrentConfiguration.RealtimeServer != null)
            {
                AVRealtime.PrintLog("use configuration websocket url:" + _wss);
                return(OpenAsync(CurrentConfiguration.RealtimeServer.ToString(), subprotocol, cancellationToken));
            }

            return(RouterController.GetAsync(CurrentConfiguration.PushRouterServer.ToString(), secure, cancellationToken).OnSuccess(_ =>
            {
                _wss = _.Result.server;
                state = Status.Connecting;
                AVRealtime.PrintLog("push router give a url :" + _wss);
                return OpenAsync(_.Result.server, subprotocol, cancellationToken);
            }).Unwrap());
        }
        /// <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()
        {
            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>
 /// Starts manual reconnect.
 /// </summary>
 public void StartManualReconnect()
 {
     state = Status.Offline;
     StartAutoReconnect();
 }
        /// <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);
                    }
                }
            }));
        }