/// <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 });
        }
Example #2
0
        /// <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));
        }