private async Task ManualStartClientConnectionLoop(TcpClient client, IManagedNetworkClient <TPayloadReadType, TPayloadWriteType> internalNetworkClient, ManagedClientSession <TPayloadReadType, TPayloadWriteType> networkSession) { //So that sessions invoking the disconnection can internally disconnect to networkSession.OnSessionDisconnection += async(source, args) => await internalNetworkClient.DisconnectAsync(0).ConfigureAwait(false); //TODO: Better way to syncronize the strategies used? var dispatchingStrategy = new InPlaceAsyncLockedNetworkMessageDispatchingStrategy <TPayloadReadType, TPayloadWriteType>(); while (client.Connected && internalNetworkClient.isConnected) { NetworkIncomingMessage <TPayloadWriteType> message = await internalNetworkClient.ReadMessageAsync(CancellationToken.None) .ConfigureAwait(false); //We don't want to stop the client just because an exception occurred. try { //TODO: This will work for World of Warcraft since it requires no more than one packet //from the same client be handled at one time. However it limits throughput and maybe we should //handle this at a different level instead. await dispatchingStrategy.DispatchNetworkMessage(new SessionMessageContext <TPayloadReadType, TPayloadWriteType>(networkSession, message)) .ConfigureAwait(false); } catch (Exception e) { //TODO: Remove this console log Logger.Error($"[Error]: {e.Message}\n\nStack: {e.StackTrace}"); } } client.Dispose(); //TODO: Should we tell the client something when it ends? await networkSession.DisconnectClientSession() .ConfigureAwait(false); }
/// <inheritdoc /> public SessionMessageContext(ManagedClientSession <TPayloadWriteType, TPayloadReadType> session, NetworkIncomingMessage <TPayloadReadType> message) { if (session == null) { throw new ArgumentNullException(nameof(session)); } if (message == null) { throw new ArgumentNullException(nameof(message)); } Session = session; Message = message; }
/// <inheritdoc /> public async Task <NetworkIncomingMessage <TReadPayloadBaseType> > ReadAsync(CancellationToken token) { using (await ReadLock.LockAsync(token).ConfigureAwait(false)) { //We want to clear the read buffers after reaiding a full message. NetworkIncomingMessage <TReadPayloadBaseType> message = await DecoratedClient.ReadAsync(token) .ConfigureAwait(false); //Do not call the object's ClearBuffer or we will deadlock; isn't re-enterant await DecoratedClient.ClearReadBuffers() .ConfigureAwait(false); //Could be null if the socket disconnected return(message); } }
/// <inheritdoc /> public abstract Task OnNetworkMessageRecieved(NetworkIncomingMessage <TPayloadReadType> message);
/// <summary> /// Reading the incoming messages from the network client and schedules them /// with the incoming message queue. /// </summary> /// <returns>A future which will complete when the client disconnects.</returns> private async Task EnqueueIncomingMessages() { //We need a token for canceling this task when a user disconnects CancellationToken incomingCancellationToken = CreateNewManagedCancellationTokenSource().Token; try { while (!incomingCancellationToken.IsCancellationRequested) { NetworkIncomingMessage <TPayloadReadType> message = await UnmanagedClient.ReadAsync(incomingCancellationToken) .ConfigureAwait(false); //If the message is null then the connection is no longer valid //The socket likely disconnected so we should stop the network thread if (message == null) { //We have to publish a null so it can be consumed by the user //to know that the socket is dead await IncomingMessageQueue.EnqueueAsync(null, CancellationToken.None) .ConfigureAwait(false); StopNetwork(); } //if have to check the token again because the message may be null and may have been canceled mid-read if (incomingCancellationToken.IsCancellationRequested) { continue; } //Try to notify interceptors of a payload that has come in. They may want it if (!InterceptorManager.TryNotifyOutstandingInterceptors(message.Payload)) { await IncomingMessageQueue.EnqueueAsync(message, incomingCancellationToken) .ConfigureAwait(false); } } } catch (TaskCanceledException e) { //This is an expected exception that happens when the token is canceled if (Logger.IsDebugEnabled) { Logger.Debug($"Expected Task Canceled Exception: {e.Message}\n\n Stack: {e.StackTrace}"); } //We cannot rethrow because this can cause application instability on threadpools } catch (Exception e) { if (Logger.IsErrorEnabled) { Logger.Error($"Error: {e.Message}\n\n Stack: {e.StackTrace}"); } //We cannot rethrow because this can cause application instability on threadpools } finally { try { await DisconnectAsync(0); } catch (Exception) { } } //TODO: Should we do anything after the dispatch has stopped? }
/// <inheritdoc /> public override Task OnNetworkMessageRecieved(NetworkIncomingMessage <TPayloadReadType> message) { //TODO: How should we handle server not having interceptor return(AuthMessageHandlerService.TryHandleMessage(MessageContextFactory.CreateMessageContext(Connection, SendService, Details), message)); }