/// <summary>
        /// Opens the <see cref="StreamingConnection"/> and listens for incoming requests, which will
        /// be assembled and sent to the provided <see cref="RequestHandler"/>.
        /// </summary>
        /// <param name="requestHandler"><see cref="RequestHandler"/> to which incoming requests will be sent.</param>
        /// <param name="cancellationToken"><see cref="CancellationToken"/> that signals the need to stop the connection.
        /// Once the token is cancelled, the connection will be gracefully shut down, finishing pending sends and receives.</param>
        /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
        public virtual async Task ListenAsync(RequestHandler requestHandler, CancellationToken cancellationToken = default)
        {
            if (requestHandler == null)
            {
                throw new ArgumentNullException(nameof(requestHandler));
            }

            var duplexPipePair = DuplexPipe.CreateConnectionPair(PipeOptions.Default, PipeOptions.Default);

            // Create transport and application
            _transport = CreateStreamingTransport(duplexPipePair.Application);
            var application = new TransportHandler(duplexPipePair.Transport, Logger);

            // Create session
            _session = new StreamingSession(requestHandler, application, Logger, cancellationToken);

            // Start transport and application
            var transportTask   = _transport.ConnectAsync(cancellationToken);
            var applicationTask = application.ListenAsync(cancellationToken);

            var tasks = new List <Task> {
                transportTask, applicationTask
            };

            // Signal that session is ready to be used
            _sessionInitializedTask.SetResult(true);

            // Let application and transport run
            await Task.WhenAll(tasks).ConfigureAwait(false);
        }
示例#2
0
        private async Task ConnectImplAsync(Func <StreamingTransport, Task> connectFunc, CancellationToken cancellationToken)
        {
            CheckDisposed();

            TimerAwaitable timer     = null;
            Task           timerTask = null;

            try
            {
                // Pipes
                var duplexPipePair = DuplexPipe.CreateConnectionPair(PipeOptions.Default, PipeOptions.Default);

                // Transport
                _transport = CreateStreamingTransport(duplexPipePair.Application);

                // Application
                _application = new TransportHandler(duplexPipePair.Transport, Logger);

                // Session
                _session = new StreamingSession(_requestHandler, _application, Logger, cancellationToken);

                // Set up cancellation
                _disconnectCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);

                // Start transport and application
                var transportTask   = connectFunc(_transport);
                var applicationTask = _application.ListenAsync(_disconnectCts.Token);
                var combinedTask    = Task.WhenAll(transportTask, applicationTask);

                Log.ClientStarted(Logger, _url);

                // Periodic task: keep alive
                // Disposed with `timer.Stop()` in the finally block below
                if (_keepAlive.HasValue)
                {
                    timer     = new TimerAwaitable(_keepAlive.Value, _keepAlive.Value);
                    timerTask = TimerLoopAsync(timer);
                }

                // We are connected!
                IsConnected = true;

                // Block until transport or application ends.
                await combinedTask.ConfigureAwait(false);

                // Signal that we're done
                _disconnectCts.Cancel();
                Log.ClientTransportApplicationCompleted(Logger, _url);
            }
            finally
            {
                timer?.Stop();

                if (timerTask != null)
                {
                    await timerTask.ConfigureAwait(false);
                }
            }

            Log.ClientCompleted(Logger, _url);
        }