Пример #1
0
        public BitConverterHelper(TcpClientIoOptions options)
        {
            _options          = options;
            _customConverters = options.Converters.Select(converter =>
            {
                var converterType = converter.GetType();
                var type          = converterType.BaseType;

                if (type == null)
                {
                    throw TcpClientIoException.ConverterError(converterType.Name);
                }

                var genericType = type.GenericTypeArguments.Single();
                return(new KeyValuePair <Type, TcpConverter>(genericType, converter));
            }).ToDictionary(pair => pair.Key, pair => pair.Value);

            _builtInConvertersToBytes = new Dictionary <Type, MethodInfo>
            {
                { typeof(bool), typeof(BitConverter).GetMethod(nameof(BitConverter.GetBytes), new[] { typeof(bool) }) },
                { typeof(char), typeof(BitConverter).GetMethod(nameof(BitConverter.GetBytes), new[] { typeof(char) }) },
                { typeof(double), typeof(BitConverter).GetMethod(nameof(BitConverter.GetBytes), new[] { typeof(double) }) },
                { typeof(short), typeof(BitConverter).GetMethod(nameof(BitConverter.GetBytes), new[] { typeof(short) }) },
                { typeof(int), typeof(BitConverter).GetMethod(nameof(BitConverter.GetBytes), new[] { typeof(int) }) },
                { typeof(long), typeof(BitConverter).GetMethod(nameof(BitConverter.GetBytes), new[] { typeof(long) }) },
                { typeof(float), typeof(BitConverter).GetMethod(nameof(BitConverter.GetBytes), new[] { typeof(float) }) },
                { typeof(ushort), typeof(BitConverter).GetMethod(nameof(BitConverter.GetBytes), new[] { typeof(ushort) }) },
                { typeof(uint), typeof(BitConverter).GetMethod(nameof(BitConverter.GetBytes), new[] { typeof(uint) }) },
                { typeof(ulong), typeof(BitConverter).GetMethod(nameof(BitConverter.GetBytes), new[] { typeof(ulong) }) }
            };
        }
Пример #2
0
        /// <summary>
        /// Begins an asynchronous request to receive response associated with the specified ID from a connected <see cref="TcpClientIo{TId,TRequest,TResponse}"/> object.
        /// </summary>
        /// <param name="responseId"></param>
        /// <param name="token"></param>
        /// <returns><see cref="ITcpBatch{TResponse}"/></returns>
        /// <exception cref="TcpClientIoException"></exception>
        public async Task <ITcpBatch <TResponse> > ReceiveAsync(TId responseId, CancellationToken token = default)
        {
            if (_disposing)
            {
                throw new ObjectDisposedException(nameof(_tcpClient));
            }

            if (!_disposing && _pipelineReadEnded)
            {
                throw TcpClientIoException.ConnectionBroken();
            }

            return(await _completeResponses.WaitAsync(responseId, token));
        }
        private void SetupTasks()
        {
            _ = TcpWriteAsync();
            _ = TcpReadAsync().ContinueWith(antecedent =>
            {
                if (_disposing || !_pipelineReadEnded)
                {
                    return;
                }

                foreach (var kv in _completeResponses.ToArray())
                {
                    kv.Value.TrySetException(TcpClientIoException.ConnectionBroken());
                }
            }, TaskContinuationOptions.OnlyOnRanToCompletion);
            _ = DeserializeResponseAsync();
        }
        private TcpClientIo(TcpClientIoOptions tcpClientIoOptions, ILogger <TcpClientIo <TId, TRequest, TResponse> > logger)
        {
            var pipe = new Pipe();

            Id          = Guid.NewGuid();
            _logger     = logger;
            _options    = tcpClientIoOptions ?? TcpClientIoOptions.Default;
            _batchRules = TcpBatchRules <TResponse> .Default;
            _baseCancellationTokenSource = new CancellationTokenSource();
            _baseCancellationToken       = _baseCancellationTokenSource.Token;
            _bufferBlockRequests         = new BufferBlock <SerializedRequest>();
            var middleware = new MiddlewareBuilder <ITcpBatch <TResponse> >()
                             .RegisterCancellationActionInWait((tcs, hasOwnToken) =>
            {
                if (_disposing || hasOwnToken)
                {
                    tcs.TrySetCanceled();
                }
                else if (!_disposing && _pipelineReadEnded)
                {
                    tcs.TrySetException(TcpClientIoException.ConnectionBroken());
                }
            })
                             .RegisterDuplicateActionInSet((batch, newBatch) => _batchRules.Update(batch, newBatch.Single()))
                             .RegisterCompletionActionInSet(() => _consumingResetEvent.Set());

            _completeResponses = new WaitingDictionary <TId, ITcpBatch <TResponse> >(middleware);
            _arrayPool         = ArrayPool <byte> .Create();

            var bitConverterHelper = new BitConverterHelper(_options);

            _serializer            = new TcpSerializer <TRequest>(bitConverterHelper, length => _arrayPool.Rent(length));
            _deserializer          = new TcpDeserializer <TId, TResponse>(bitConverterHelper);
            _writeResetEvent       = new AsyncManualResetEvent();
            _readResetEvent        = new AsyncManualResetEvent();
            _consumingResetEvent   = new AsyncManualResetEvent();
            _deserializePipeReader = pipe.Reader;
            _deserializePipeWriter = pipe.Writer;
        }
Пример #5
0
        /// <summary>
        /// Serialize and sends data asynchronously to a connected <see cref="TcpClientIo{TRequest,TResponse}"/> object.
        /// </summary>
        /// <param name="request"></param>
        /// <param name="token"></param>
        /// <returns><see cref="bool"/></returns>
        /// <exception cref="TcpClientIoException"></exception>
        public async Task <bool> SendAsync(TRequest request, CancellationToken token = default)
        {
            try
            {
                if (_disposing)
                {
                    throw new ObjectDisposedException(nameof(_tcpClient));
                }

                if (!_disposing && _pipelineWriteEnded)
                {
                    throw TcpClientIoException.ConnectionBroken();
                }

                var serializedRequest = _serializer.Serialize(request);
                return(await _bufferBlockRequests.SendAsync(serializedRequest, token == default?_baseCancellationToken : token));
            }
            catch (Exception e)
            {
                _logger?.LogError($"{nameof(SendAsync)} Got {e.GetType()}: {e.Message}");
                throw;
            }
        }
Пример #6
0
        /// <summary>Provides a consuming <see cref="T:System.Collections.Generics.IAsyncEnumerable{T}"/> for <see cref="ITcpBatch{TResponse}"/> in the collection.
        /// Calling MoveNextAsync on the returned enumerable will block if there is no data available, or will
        /// throw an <see cref="System.OperationCanceledException"/> if the <see cref="CancellationToken"/> is canceled.
        /// </summary>
        /// <param name="token">A cancellation token to observe.</param>
        /// <returns></returns>
        /// <exception cref="OperationCanceledException">If the <see cref="CancellationToken"/> is canceled.</exception>
        /// <exception cref="TcpClientIoException"></exception>
        public async IAsyncEnumerable <ITcpBatch <TResponse> > GetConsumingAsyncEnumerable([EnumeratorCancellation] CancellationToken token = default)
        {
            CancellationTokenSource internalCts = null;
            CancellationToken       internalToken;

            if (token != default)
            {
                internalCts   = CancellationTokenSource.CreateLinkedTokenSource(_baseCancellationToken, token);
                internalToken = internalCts.Token;
            }
            else
            {
                internalToken = _baseCancellationToken;
            }

            while (!internalToken.IsCancellationRequested)
            {
                IList <ITcpBatch <TResponse> > result;

                try
                {
                    internalToken.ThrowIfCancellationRequested();

                    var completedResponses = _completeResponses
                                             .Filter(p => p.Value.Task.Status == TaskStatus.RanToCompletion)
                                             .ToArray();

                    if (completedResponses.Length == 0)
                    {
                        await _consumingResetEvent.WaitAsync(internalToken);

                        _consumingResetEvent.Reset();
                        continue;
                    }

                    result = new List <ITcpBatch <TResponse> >();

                    foreach (var(key, tcs) in completedResponses)
                    {
                        internalToken.ThrowIfCancellationRequested();

                        if (await _completeResponses.TryRemoveAsync(key))
                        {
                            result.Add(await tcs.Task);
                        }
                    }
                }
                catch (OperationCanceledException)
                {
                    if (!_disposing && _pipelineReadEnded)
                    {
                        throw TcpClientIoException.ConnectionBroken();
                    }

                    throw;
                }
                catch (Exception exception)
                {
                    _logger?.LogCritical($"{nameof(GetConsumingAsyncEnumerable)} Got {exception.GetType()}, {exception}");
                    throw;
                }

                foreach (var batch in result)
                {
                    yield return(batch);
                }
            }

            internalCts?.Dispose();
        }