/// <nodoc /> public MultiplexingClient(IClientConfig config, Stream stream) { Contract.Requires(config != null); Contract.Requires(stream != null); Config = config; Logger = config.Logger ?? VoidLogger.Instance; m_stream = stream; m_pendingRequests = new ConcurrentDictionary <int, TaskSourceSlim <IIpcResult> >(); m_disconnectRequestedByServer = false; // Receiving Dataflow: // This generic server will call 'SetResponse' for every received Response. 'SetResponse' // could be done in parallel, but there is no point, since they all access a shared concurrent // dictionary and all they do is lookup task completion source and set a result for it. m_responseListener = new GenericServer <Response>( name: "MultiplexingClient.ResponseListener", config: new ServerConfig { Logger = Logger, MaxConcurrentClients = 1 }, listener: ReceiveResponseAsync, clientFailuresAreFatal: true); // Sending Dataflow: // All 'Send' requests are concurrently queued up here. Processing of this block // ('SendRequestAsync') must be sequential, because uses the shared connection. m_sendRequestBlock = new ActionBlock <Request>( SendRequestAsync, new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = 1, }); // set continuations that handle errors (unblock pending requests and set failure as completion of this client) var continuationDone = TaskSourceSlim.Create <Unit>(); m_responseListener.Completion.ContinueWith(_ => m_sendRequestBlock.Complete()); m_sendRequestBlock.Completion.ContinueWith(async _ => { m_responseListener.Complete(); UnblockPendingRequests(new IpcResult(IpcResultStatus.GenericError, "MultiplexingClient completed before all requests were handled")); if (!m_disconnectRequestedByServer) { var succeeded = await Request.StopRequest.TrySerializeAsync(m_stream); Logger.Verbose("Sending StopRequest {0}.", succeeded ? "Succeeded" : "Failed"); } continuationDone.SetResult(Unit.Void); }); // start listening for responses m_responseListener.Start(SetResponseAsync); // set the completion task m_completion = TaskUtilities.SafeWhenAll(new[] { m_sendRequestBlock.Completion, m_responseListener.Completion, continuationDone.Task }); }
/// <nodoc/> public NonMultiplexingServer([CanBeNull] string name, IServerConfig config, IConnectivityProvider <TClient> connectivityProvider) { Contract.Requires(config != null); Contract.Requires(connectivityProvider != null); Name = name ?? GetType().Name; m_connectivityProvider = connectivityProvider; m_clientListener = new GenericServer <TClient>( name: Name, config: config, listener: connectivityProvider.AcceptClientAsync); }
/// <nodoc/> public RequestHandler(MultiplexingServer <TClient> parent, TClient client) { m_parent = parent; m_client = client; m_stream = parent.m_connectivityProvider.GetStreamForClient(client); Name = I($"{parent.Name}.RequestHandler({parent.m_connectivityProvider.Describe(client)})"); m_stopRequestedByClient = false; m_requestListener = new GenericServer <Request>( name: Name, config: m_parent.m_requestHandlingConfig, listener: AcceptRequestAsync); m_sendResponseBlock = new ActionBlock <Response>( SendResponseAsync, new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = 1 }); // streaming must be sequential // chain block completions: either one completes the other var continuationDone = TaskSourceSlim.Create <Unit>(); m_requestListener.Completion.ContinueWith(_ => m_sendResponseBlock.Complete()); m_sendResponseBlock.Completion.ContinueWith(async _ => { m_requestListener.Complete(); if (!m_stopRequestedByClient) { var succeeded = await Response.DisconnectResponse.TrySerializeAsync(m_stream); Logger.Verbose("({0}) Sending DisconnectResponse {1}.", Name, succeeded ? "Succeeded" : "Failed"); } Logger.Verbose("({0}) Disconnecting client...", Name); bool ok = TryDisconnectClient(m_client); Logger.Verbose("({0}) Disconnecting client {1}.", Name, ok ? "Succeeded" : "Failed"); continuationDone.SetResult(Unit.Void); }); // set the completion task to be the completion of both listener and sender blocks, as well as the cleanup continuation m_completion = TaskUtilities.SafeWhenAll(new[] { m_requestListener.Completion, m_sendResponseBlock.Completion, continuationDone.Task }); }
/// <nodoc/> public MultiplexingServer([CanBeNull] string name, [CanBeNull] ILogger logger, IConnectivityProvider <TClient> connectivityProvider, int maxConcurrentClients, int maxConcurrentRequestsPerClient) { Contract.Requires(connectivityProvider != null); Contract.Requires(maxConcurrentClients > 0); Contract.Requires(maxConcurrentRequestsPerClient > 0); Name = name ?? GetType().Name; Logger = logger ?? VoidLogger.Instance; m_connectivityProvider = connectivityProvider; m_clientHandlingConfig = new ServerConfig { Logger = Logger, MaxConcurrentClients = maxConcurrentClients }; m_requestHandlingConfig = new ServerConfig { Logger = Logger, MaxConcurrentClients = maxConcurrentRequestsPerClient }; m_clientListener = new GenericServer <TClient>( name: Name + ".ClientHandler", config: m_clientHandlingConfig, listener: (token) => connectivityProvider.AcceptClientAsync(token)); }