简体   繁体   English

恢复使用Rx的异步读取?

[英]Resuming an async read using Rx?

Another Rx question today :) 今天的另一个Rx问题:)

Simply put, I'm using asynchronous IO to operate on Streams. 简而言之,我正在使用异步IO在Streams上进行操作。 However, as we all know, we do not necessarily get all the bytes we want when using an asynchronous read - hence the return of an int on XAsync methods. 但是,众所周知,使用异步读取时并不一定要获得所有想要的字节,因此XAsync方法上返回的是int。 I was wondering how could I tell an Rx Observable to retry a read that did not read the right amount of bytes from the Stream and offset by the correct amount? 我想知道如何告诉Rx Observable重试未从Stream中读取正确数量的字节并偏移正确数量的读取?

Currently, I have this, but no idea how to set the offset parameter in ReadAsync. 目前,我有这个,但是不知道如何在ReadAsync中设置offset参数。

    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

Simplest way is to just use a local variable in a closure, plus Defer to force the observable to re-evaluate its function on each iteration. 最简单的方法是在闭包中使用局部变量,再加上Defer强制可观察对象在每次迭代时重新评估其功能。

Assuming you want to resume reading the next block after the current block finishes, you end up with something like this... 假设您要在当前块完成后继续读取下一个块,您将得到如下所示的结果...

// 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());
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM