/// <summary>Creates a new client. A client can manage one connection to a server.</summary> /// <param name="dispatcherType">The message processing method for incomming notifications. /// See <see cref="EventDispatchType"/> for further information about each type.</param> public TsFullClient(DedicatedTaskScheduler?scheduler = null) { status = TsClientStatus.Disconnected; msgProc = new AsyncMessageProcessor(MessageHelper.GetToClientNotificationType); this.scheduler = scheduler ?? new DedicatedTaskScheduler(Id.Null); this.isOwnScheduler = scheduler is null; }
/// <summary>Creates a new client. A client can manage one connection to a server.</summary> /// <param name="dispatcherType">The message processing method for incomming notifications. /// See <see cref="EventDispatchType"/> for further information about each type.</param> public TsFullClient() { status = TsClientStatus.Disconnected; msgProc = new AsyncMessageProcessor(MessageHelper.GetToClientNotificationType); context = new ConnectionContext { WasExit = true }; }
partial void ProcessEachInitServer(InitServer initServer) { packetHandler.ClientId = initServer.ClientId; lock (statusLock) status = TsClientStatus.Connected; OnConnected?.Invoke(this, EventArgs.Empty); }
private void ChangeState(ConnectionContext ctx, TsClientStatus setStatus, CommandError?error = null) { scheduler.VerifyOwnThread(); if (ctx != context) { Log.Debug("Stray disconnect from old packethandler"); } Log.Debug("ChangeState {0} -> {1} (error:{2})", status, setStatus, error?.ErrorFormat() ?? "none"); switch ((status, setStatus)) { case (TsClientStatus.Disconnected, TsClientStatus.Disconnected): // Already disconnected, do nothing break; case (TsClientStatus.Disconnected, TsClientStatus.Connecting): status = TsClientStatus.Connecting; break; case (TsClientStatus.Connecting, TsClientStatus.Connected): status = TsClientStatus.Connected; ctx.ConnectEvent.SetResult(R.Ok); break; case (TsClientStatus.Connecting, TsClientStatus.Disconnected): case (TsClientStatus.Connected, TsClientStatus.Disconnected): case (TsClientStatus.Disconnecting, TsClientStatus.Disconnected): status = TsClientStatus.Disconnected; ctx.PacketHandler.Stop(); msgProc.DropQueue(); var statusBefore = status; context = null; if (statusBefore == TsClientStatus.Connecting) { ctx.ConnectEvent.SetResult(error ?? CommandError.ConnectionClosed); // TODO: Set exception maybe ? } ctx.DisconnectEvent.SetResult(null); OnDisconnected?.Invoke(this, new DisconnectEventArgs(ctx.ExitReason ?? Reason.LeftServer, error)); break; case (TsClientStatus.Connected, TsClientStatus.Disconnecting): status = TsClientStatus.Disconnecting; break; default: Trace.Fail($"Invalid transition change from {status} to {setStatus}"); break; } }
private void DisconnectInternal(ConnectionContext ctx, CommandError error = null, TsClientStatus?setStatus = null) { bool triggerEventSafe = false; lock (statusLock) { Log.Debug("DisconnectInternal wasExit:{0} error:{1} oldStatus:{2} newStatus:{3}", ctx.WasExit, error?.ErrorFormat(), status, setStatus); if (setStatus.HasValue) { status = setStatus.Value; } if (ctx.WasExit) { return; } switch (status) { case TsClientStatus.Connecting: case TsClientStatus.Disconnected: ctx.WasExit = true; packetHandler.Stop(); msgProc.DropQueue(); dispatcher.Dispose(); dispatcher = null; triggerEventSafe = true; break; case TsClientStatus.Disconnecting: break; case TsClientStatus.Connected: ClientDisconnect(Reason.LeftServer, QuitMessage); status = TsClientStatus.Disconnecting; break; default: throw Tools.UnhandledDefault(status); } } if (triggerEventSafe) { OnDisconnected?.Invoke(this, new DisconnectEventArgs(ctx.ExitReason ?? Reason.LeftServer, error)); } }
/// <summary>Tries to connect to a server.</summary> /// <param name="conData">Set the connection information properties as needed. /// For further details about each setting see the respective property documentation in <see cref="ConnectionData"/></param> /// <exception cref="ArgumentException">When some required values are not set or invalid.</exception> /// <exception cref="TsException">When the connection could not be established.</exception> public override void Connect(ConnectionData conData) { if (!(conData is ConnectionDataFull conDataFull)) { throw new ArgumentException($"Use the {nameof(ConnectionDataFull)} derivative to connect with the full client.", nameof(conData)); } if (conDataFull.Identity is null) { throw new ArgumentNullException(nameof(conDataFull.Identity)); } if (conDataFull.VersionSign is null) { throw new ArgumentNullException(nameof(conDataFull.VersionSign)); } connectionDataFull = conDataFull; ConnectionData = conData; Disconnect(); if (!TsDnsResolver.TryResolve(conData.Address, out remoteAddress)) { throw new TsException("Could not read or resolve address."); } lock (statusLock) { returnCode = 0; status = TsClientStatus.Connecting; VersionSign = conDataFull.VersionSign; tsCrypt = new TsCrypt(); tsCrypt.Identity = conDataFull.Identity; var ctx = new ConnectionContext { WasExit = false }; context = ctx; packetHandler = new PacketHandler <S2C, C2S>(tsCrypt, conData.LogId); packetHandler.PacketEvent = (ref Packet <S2C> packet) => { PacketEvent(ctx, ref packet); }; packetHandler.StopEvent = (closeReason) => { ctx.ExitReason = closeReason; DisconnectInternal(ctx, setStatus: TsClientStatus.Disconnected); }; packetHandler.Connect(remoteAddress); dispatcher = new ExtraThreadEventDispatcher(); dispatcher.Init(InvokeEvent, conData.LogId); } }