Возобновление асинхронного чтения с использованием Rx?

1

Еще один вопрос Rx сегодня :)

Проще говоря, я использую асинхронный ввод-вывод для работы с Streams. Однако, как мы все знаем, мы не обязательно получаем все нужные байты при использовании асинхронного чтения - следовательно, возвращаем XAsync int по методам XAsync. Мне было интересно, как я могу сказать Rx Observable чтобы повторить чтение, которое не читало правильное количество байтов из потока и смещено на правильную сумму?

В настоящее время у меня есть это, но не знаю, как установить параметр offset в ReadAsync.

    private IDisposable _streamMessageContract;
    private readonly byte[] _readBuffer = new byte[8024];

    public void Start()
    {
        // Subscribe to the stream for dataz
        _streamMessageContract = Observable.FromAsync<int>(() => _stream.ReadAsync(_readBuffer, 0, _readBuffer.Length))
            .Repeat()
            .Subscribe(
                y => _RawBytesReceived(_readBuffer, y),
               ex => _Exception(ex),
               () => _StreamClosed());
    }

    #region Helpers
    private void _RawBytesReceived(byte[] bytes, int actualBytesRead)
    {
    }
    private void _StreamClosed()
    {
    }
    private void _Exception(Exception e)
    {
    }
    #endregion
Теги:
system.reactive

1 ответ

1
Лучший ответ

Самый простой способ - просто использовать локальную переменную в закрытии, плюс Defer чтобы заставить наблюдаемую переоценивать свою функцию на каждой итерации.

Предполагая, что вы хотите возобновить чтение следующего блока после завершения текущего блока, вы получите что-то вроде этого...

// An observable that will yield the next block in the stream.
// If the resulting block length is less than blockSize, then the
// end of the stream was reached.
private IObservable<byte[]> ReadBlock(Stream stream, int blockSize)
{
    // Wrap the whole thing in Defer() so that we
    // get a new memory block each time this observable is subscribed.
    return Observable.Defer(() =>
    {
        var block = new byte[blockSize];
        int numRead = 0;
        return Observable
            .Defer(() => Observable.FromAsync<int>(() =>
                stream.ReadAsync(block, numRead, block.Length - numRead)))
            .Do(y -=> numRead += y)
            .Repeat()
            .TakeWhile(y => y > 0 && numRead != blockSize) // take until EOF or entire block is read
            .LastOrDefaultAsync()  // only emit the final result of this operation
            .Select(_ =>
            {
                // If we hit EOF, then resize our result array to
                // the number of bytes actually read
                if (numRead < blockSize)
                {
                    block = block.Take(numRead).ToArray();
                }

                return block;
            });
    });
}

public void Start()
{
    // Subscribe to the stream for dataz.
    // Just keep reading blocks until
    // we get the final (under-sized) block
    _streamMessageContract = ReadBlock(stream, 8024)
        .Repeat()
        .TakeWhile(block => block.Length == 8024) // if block is small then that means we hit the end of the stream
        .Subscribe(
           block => _RawBytesReceived(block),
           ex => _Exception(ex),
           () => _StreamClosed());
}
  • 0
    Единственная проблема заключается в том, что 8024 - это блок максимального размера , я не использую пакеты фиксированной длины, я использую пакеты с префиксной длиной, поэтому пакеты различаются по размеру.
  • 0
    Затем вызовите ReadBlock с размером блока 4 или любым другим размером префикса. Затем используйте SelectMany для повторного вызова ReadBlock с длиной префикса, которую вы только что прочитали. Что-то вроде ReadBlock(stream, 4).SelectMany(p => p == null ? Observable.Return<byte[]>(null) : ReadBlock(stream, BitConverter.ToInt32(p, 0)).Repeat().TakeWhile(message => message != null) и изменить ReadBlock испускать null , когда он встречает EOF вместо испускать массив меньшего размера.
Показать ещё 6 комментариев

Ещё вопросы

Сообщество Overcoder
Наверх
Меню