使用Rx恢复异步读取?

时间:2014-05-21 20:19:32

标签: c# system.reactive

今天另一个Rx问题:)

简单地说,我正在使用异步IO来操作Streams。但是,众所周知,在使用异步读取时,我们不一定得到所需的所有字节 - 因此在XAsync方法上返回int。我想知道如何告诉Rx Observable重试读取没有从Stream中读取正确数量的字节并以正确的数量偏移?

目前,我有这个,但不知道如何在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

1 个答案:

答案 0 :(得分:1)

最简单的方法是在闭包中使用局部变量,加上Defer强制observable在每次迭代时重新计算其函数。

假设你想在当前程序段结束后继续阅读下一个程序段,你最终会得到类似的东西......

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