/// <summary> /// 重要:连接服务端,一般做为入口方法 /// 该方法主要操作一些配置和心跳 /// AlwaysReconnect:始终重试,开启此选项,无论何时,一旦程序在连接不上时都会进行重试,否则只在连接成功后的异常中断时才重试。 /// </summary> /// <returns></returns> public async Task Start(bool AlwaysReconnect = false) { if (AlwaysReconnect) { IsStarted = true; } var oneLiveToken = ONE_LIVE_TOKEN_SRC.Token; //登录功能 string arrangedToken = Global.NO_TOKEN_STRING; while (!oneLiveToken.IsCancellationRequested) { CANCEL_TOKEN_SRC = new CancellationTokenSource(); TRANSFERING_TOKEN_SRC = new CancellationTokenSource(); HEARTBEAT_TOKEN_SRC = new CancellationTokenSource(); _waiter = new TaskCompletionSource <object>(); Router.TimeStamp = Guid.NewGuid(); var appIdIpPortConfig = ClientConfig.Clients; int clientId = 0; //0 获取服务器端口配置 try { await InitServerPorts(); } catch (Exception ex)//出错 重连 { if (IsStarted == false) { StatusChanged(ClientStatus.LoginError, null); return; } else { Logger.Error("获取服务器端口失败:" + ex.Message, ex); await Task.Delay(Global.ClientReconnectInterval, ONE_LIVE_TOKEN_SRC.Token); continue; } } //0.5 处理登录/重登录/匿名登录逻辑 try { //登录 if (CurrentLoginInfo != null) { var loginResult = await Login(); arrangedToken = loginResult.Item1; clientId = loginResult.Item2; } else if (File.Exists(NspClientCachePath)) { //登录缓存 arrangedToken = File.ReadAllText(NspClientCachePath); //TODO 这个token的合法性无法保证,如果服务端删除了用户,而这里缓存还存在,会导致无法登录 //TODO ***** 这是个trick:防止匿名用户被服务端踢了之后无限申请新账号 //TODO 待解决 版本号无法显示的问题 CurrentLoginInfo = null; } else { //匿名登录,未提供登录信息时,使用空用户名密码自动注册并尝试匿名登录 Router.Logger.Debug("未提供登录信息,尝试匿名登录"); CurrentLoginInfo = new LoginInfo() { UserName = "", UserPwd = "" }; var loginResult = await Login(); arrangedToken = loginResult.Item1; clientId = loginResult.Item2; //保存缓存到磁盘 File.WriteAllText(NspClientCachePath, arrangedToken); } } catch (Exception ex)//出错 重连 { if (IsStarted == false) { StatusChanged(ClientStatus.LoginError, null); return; } else { Logger.Error("启动失败:" + ex.Message, ex); await Task.Delay(Global.ClientReconnectInterval, ONE_LIVE_TOKEN_SRC.Token); continue; } } //1.获取配置 ConnectionManager = ServerConnectionManager.Create(clientId); ConnectionManager.CurrentToken = arrangedToken; ConnectionManager.ClientGroupConnected += ServerConnnectionManager_ClientGroupConnected; ConnectionManager.ServerNoResponse = DoServerNoResponse; //下钻事件 ClientModel clientModel = null; // try { //从服务端初始化客户端配置 clientModel = await ConnectionManager.InitConfig(this.ClientConfig).ConfigureAwait(false); } catch (Exception ex) { //TODO 状态码:连接失败 Router.Logger.Error("连接失败:" + ex.Message, ex); //throw; } //HasConnected = true; if (clientModel != null) { int counter = 0; //2.分配配置:appid为0时说明没有分配appid,所以需要分配一个 foreach (var app in appIdIpPortConfig) { if (app.AppId == 0) { app.AppId = clientModel.AppList[counter].AppId; counter++; } } Logger.Debug("****************port list*************"); List <string> tunnelstrs = new List <string>(); foreach (var ap in clientModel.AppList) { var cApp = appIdIpPortConfig.First(obj => obj.AppId == ap.AppId); var tunnelStr = ap.AppId.ToString() + ": " + ClientConfig.ProviderAddress + ":" + ap.Port.ToString() + "=>" + cApp.IP + ":" + cApp.TargetServicePort; Logger.Debug(tunnelStr); tunnelstrs.Add(tunnelStr); } Logger.Debug("**************************************"); _ = ConnectionManager.PollingToProvider(StatusChanged, tunnelstrs); //3.创建心跳连接 _ = ConnectionManager.StartHeartBeats(Global.HeartbeatInterval, HEARTBEAT_TOKEN_SRC.Token, _waiter); IsStarted = true; if (await _waiter.Task.ConfigureAwait(false) is Exception exception) { Router.Logger.Debug($"程序异常终止:{exception.Message}。"); } else { Router.Logger.Debug($"未知异常。"); } } else { Router.Logger.Debug($"程序启动失败。"); //如果程序从未启动过就出错,则终止程序,否则重试。 if (IsStarted == false) { StatusChanged(ClientStatus.Stopped, null); return; } } Router.Logger.Debug($"连接故障,尝试关闭连接并重试"); if (ConnectionManager != null) { ConnectionManager.CloseAllConnections();//关闭所有连接 } //出错重试 await Task.Delay(Global.ClientReconnectInterval, ONE_LIVE_TOKEN_SRC.Token); //TODO 返回错误码 //await Task.Delay(TimeSpan.FromHours(24), CANCEL_TOKEN.CurrentToken).ConfigureAwait(false); Router.Logger.Debug($"连接关闭,开启重试"); } //正常终止 Router.Logger.Debug($"停止重试,循环终止。"); }
/// <summary> /// 重要:连接服务端,一般做为入口方法 /// 该方法主要操作一些配置和心跳 /// AlwaysReconnect:始终重试,开启此选项,无论何时,一旦程序在连接不上时都会进行重试,否则只在连接成功后的断线时才重试。 /// </summary> /// <returns></returns> public async Task Start(bool AlwaysReconnect = false) { if (AlwaysReconnect) { IsStarted = true; } var oneLiveToken = ONE_LIVE_TOKEN_SRC.Token; //登陆功能 string arrangedToken = Global.NO_TOKEN_STRING; while (!oneLiveToken.IsCancellationRequested) { CANCEL_TOKEN_SRC = new CancellationTokenSource(); TRANSFERING_TOKEN_SRC = new CancellationTokenSource(); HEARTBEAT_TOKEN_SRC = new CancellationTokenSource(); _waiter = new TaskCompletionSource <object>(); var appIdIpPortConfig = ClientConfig.Clients; int clientId = 0; //0.5 如果有文件,取出缓存中的clientid try { //登陆缓存 if (File.Exists(NSMART_CLIENT_CACHE_PATH)) { using (var stream = File.OpenRead(NSMART_CLIENT_CACHE_PATH)) { byte[] bytes = new byte[2]; stream.Read(bytes, 0, bytes.Length); clientId = StringUtil.DoubleBytesToInt(bytes); } } //登陆 if (CurrentLoginInfo != null) { NSPDispatcher disp = new NSPDispatcher(); var result = await disp.LoginFromClient(CurrentLoginInfo.UserName, CurrentLoginInfo.UserPwd); if (result.State == 1) { Router.Logger.Debug("登陆成功"); var data = result.Data; arrangedToken = data.Token; Router.Logger.Debug($"服务端版本号:{data.Version},当前适配版本号{Global.NSmartProxyServerName}"); clientId = int.Parse(data.Userid); } else { throw new Exception("登陆失败,服务端返回错误如下:" + result.Msg); } } else { Router.Logger.Debug("为提供登陆信息,尝试匿名登陆"); } } catch (Exception ex) { Logger.Error(ex.Message, ex); } //1.获取配置 ConnectionManager = ServerConnectionManager.Create(clientId); ConnectionManager.CurrentToken = arrangedToken; ConnectionManager.ClientGroupConnected += ServerConnnectionManager_ClientGroupConnected; ConnectionManager.ServerNoResponse = DoServerNoResponse; //下钻事件 ClientModel clientModel = null; // try { //非第一次则算作重连,发送clientid过去 clientModel = await ConnectionManager.InitConfig(this.ClientConfig).ConfigureAwait(false); } catch (Exception ex) { //TODO 状态码:连接失败 Router.Logger.Error("连接失败:" + ex.Message, ex); //throw; } //HasConnected = true; if (clientModel != null) { int counter = 0; //1.5 写入缓存 File.WriteAllBytes(NSMART_CLIENT_CACHE_PATH, StringUtil.IntTo2Bytes(clientModel.ClientId)); //2.分配配置:appid为0时说明没有分配appid,所以需要分配一个 foreach (var app in appIdIpPortConfig) { if (app.AppId == 0) { app.AppId = clientModel.AppList[counter].AppId; counter++; } } Logger.Debug("****************port list*************"); List <string> tunnelstrs = new List <string>(); foreach (var ap in clientModel.AppList) { var cApp = appIdIpPortConfig.First(obj => obj.AppId == ap.AppId); var tunnelStr = ap.AppId.ToString() + ": " + ClientConfig.ProviderAddress + ":" + ap.Port.ToString() + "=>" + cApp.IP + ":" + cApp.TargetServicePort; Logger.Debug(tunnelStr); tunnelstrs.Add(tunnelStr); } Logger.Debug("**************************************"); ConnectionManager.PollingToProvider(StatusChanged, tunnelstrs); //3.创建心跳连接 ConnectionManager.StartHeartBeats(Global.HeartbeatInterval, HEARTBEAT_TOKEN_SRC.Token); IsStarted = true; Exception exception = await _waiter.Task.ConfigureAwait(false) as Exception; if (exception != null) { Router.Logger.Debug($"程序异常终止:{exception.Message}。"); } else { Router.Logger.Debug($"未知异常。"); } } else { Router.Logger.Debug($"程序启动失败。"); //如果程序从未启动过就出错,则终止程序,否则重试。 if (IsStarted == false) { StatusChanged(ClientStatus.Stopped, null); return; } } Router.Logger.Debug($"连接故障,尝试关闭连接并重试"); if (ConnectionManager != null) { ConnectionManager.CloseAllConnections();//关闭所有连接 } //出错重试 await Task.Delay(Global.ClientReconnectInterval, ONE_LIVE_TOKEN_SRC.Token); //TODO 返回错误码 //await Task.Delay(TimeSpan.FromHours(24), CANCEL_TOKEN.CurrentToken).ConfigureAwait(false); Router.Logger.Debug($"连接关闭,开启重试"); } //正常终止 Router.Logger.Debug($"停止重试,循环终止。"); }