/// <inheritdoc /> public virtual async Task DisconnectAsync(int delay) { await UnmanagedClient.DisconnectAsync(delay) .ConfigureAwait(false); isConnected = false; }
/// <inheritdoc /> public override async Task <SendResult> SendMessageImmediately <TPayloadType>(TPayloadType payload, DeliveryMethod method) { await UnmanagedClient.WriteAsync(payload) .ConfigureAwait(false); return(SendResult.Sent); }
/// <inheritdoc /> public override async Task <SendResult> SendMessage <TPayloadType>(TPayloadType payload, DeliveryMethod method) { if (this.isConnected) { try { //TODO: Handle delivery method //TODO: What should we do when this is being called during a critical section? Won't we want to queue this up so serialization and encryption //doesn't block? await UnmanagedClient.WriteAsync(payload) .ConfigureAwait(false); } catch (NetworkDisconnectedException e) { //The client/session/network that the caller //was trying to send a message to is disconnected. This doesn't mean //we want to throw though. Catching exceptions is EXPENSIVE but this should only happen on a rare occasion //and the caller will see that the client is disconnected, and make decisions based on that //and of course disconnection logic will likely be happening else where during this return(SendResult.Disconnected); } //We should let other exceptions be thrown though, as they aren't related to connectivity. } else { return(SendResult.Disconnected); } return(SendResult.Sent); }
/// <inheritdoc /> public Task WriteAsync(byte[] bytes, int offset, int count) { //TODO: This is a hack, but it was the quickest way to implement this feature. We need to bypass the highlevel networking sometimes. try { return(((IBytesWrittable)UnmanagedClient).WriteAsync(bytes, offset, count)); } catch (Exception e) { throw new InvalidOperationException($"Cannot write bytes to Type: {UnmanagedClient.GetType().Name}. Does not imlpement {nameof(IBytesWrittable)}. Exception: {e.Message} \n\n Stack: {e.StackTrace}", e); } }
/// <inheritdoc /> public virtual async Task <bool> ConnectAsync(string address, int port) { //Disconnect if we're already connected if (isConnected) { await DisconnectAsync(0) .ConfigureAwait(false); } //This COULD return false, so we need to handle that isConnected = await UnmanagedClient.ConnectAsync(address, port) .ConfigureAwait(false); return(isConnected); }
/// <summary> /// Dispatches the outgoing messages scheduled to be send /// over the network. /// </summary> /// <returns>A future which will complete when the client disconnects.</returns> private async Task DispatchOutgoingMessages() { try { //We need a token for canceling this task when a user disconnects CancellationToken dispatchCancelation = CreateNewManagedCancellationTokenSource().Token; while (!dispatchCancelation.IsCancellationRequested) { TPayloadWriteType payload = await OutgoingMessageQueue.DequeueAsync(dispatchCancelation) .ConfigureAwait(false); await UnmanagedClient.WriteAsync(payload) .ConfigureAwait(false); } } catch (TaskCanceledException e) { //Supress this exception. We don't care about it. It's expected. //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? }
/// <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 <NetworkIncomingMessage <TPayloadReadType> > ReadMessageAsync(CancellationToken token) { return(UnmanagedClient.ReadAsync(token)); }