C# 异步的。从NetworkStream块读取60秒。(Rx)

C# 异步的。从NetworkStream块读取60秒。(Rx),c#,system.reactive,asyncsocket,networkstream,C#,System.reactive,Asyncsocket,Networkstream,当流中没有数据时,我尝试读取流块60秒。 当存在一些数据时,读取将按需要完成。 如何重写以下代码,使其在stream.DataAvailable为true时只能读取 我想我需要像Observable.While(dataAvailableObserver,AsyncRead)这样的东西 publicstaticiobservable异步读取(此NetworkStream流,int-bufferSize) { 返回可观察的。创建( o=>Observable.Defer(()=>AsyncRead

当流中没有数据时,我尝试读取流块60秒。 当存在一些数据时,读取将按需要完成。 如何重写以下代码,使其在stream.DataAvailable为true时只能读取

我想我需要像Observable.While(dataAvailableObserver,AsyncRead)这样的东西

publicstaticiobservable异步读取(此NetworkStream流,int-bufferSize)
{
返回可观察的。创建(
o=>Observable.Defer(()=>AsyncReadChunk(流,缓冲区大小))
.重复
.Subscribe(数据块=>
{
如果(dataChunk.Length>0)
{
o、 OnNext(数据块);
返回;
}
Debug.Assert(!stream.DataAvailable);
o、 未完成();
},o.OnError,o.OnCompleted));
}
公共静态IObservable AsyncReadChunk(此NetworkStream流,int bufferSize)
{
var buffer=新字节[bufferSize];
返回Observable.FromAsyncPattern(stream.BeginRead,stream.EndRead)(缓冲区,0,缓冲区大小)
.选择(cbRead=>
{
WriteLine(“接收到数据块”);
var dataChunk=新字节[cbRead];
块复制(Buffer,0,dataChunk,0,cbRead);
返回数据块;
});
}

我发现读取较小的缓冲区大小,因为较大的缓冲区会导致缓冲区的等待被填满(就像在我的场景中,传入的数据是小数据包)。

因为您使用的是延迟,所以必须检查延迟逻辑中的可用数据。最简单的方法是在AsyncReadChunk方法内执行检查,如:

public static IObservable<byte[]> AsyncReadChunk(this NetworkStream stream, int bufferSize) 
{ 
    if (!stream.DataAvailable)
    {
        return Observable.Empty<byte[]>();
    }
    else
    {
        var buffer = new byte[bufferSize]; 

        return Observable.FromAsyncPattern<byte[], int, int, int>(stream.BeginRead, stream.EndRead)(buffer, 0, bufferSize) 
            .Select(cbRead => 
公共静态IObservable AsyncReadChunk(此NetworkStream流,int bufferSize)
{ 
如果(!stream.DataAvailable)
{
return-Observable.Empty();
}
其他的
{
var buffer=新字节[bufferSize];
返回Observable.FromAsyncPattern(stream.BeginRead,stream.EndRead)(缓冲区,0,缓冲区大小)
.选择(cbRead=>

我有点不确定这是否有帮助,但这是我用来读取流的ToObservable()方法

public static class ObservableApmExtensions
{
    public static IObservable<byte> ToObservable(this FileStream source)
    {
        return source.ToObservable(4096, Scheduler.CurrentThread);
    }

    public static IObservable<byte> ToObservable(this FileStream source, int buffersize, IScheduler scheduler)
    {
        return Observable.Create<byte>(o =>
        {
            var initialState = new StreamReaderState(source, buffersize);
            var subscription = new MultipleAssignmentDisposable();
            Action<StreamReaderState, Action<StreamReaderState>> action =
                (state, self) =>
                {
                    subscription.Disposable = state.ReadNext()
                        .Subscribe(
                            bytesRead =>
                            {
                                for (int i = 0; i < bytesRead; i++)
                                {
                                    o.OnNext(state.Buffer[i]);
                                }
                                if (bytesRead > 0)
                                    self(state);
                                else
                                    o.OnCompleted();
                            },
                            o.OnError);
                };

            var scheduledAction = scheduler.Schedule(initialState, action);
            return new CompositeDisposable(scheduledAction, subscription);
        });
    }

    private sealed class StreamReaderState
    {
        private readonly int _bufferSize;
        private readonly Func<byte[], int, int, IObservable<int>> _factory;

        public StreamReaderState(Stream source, int bufferSize)
        {
            _bufferSize = bufferSize;
            _factory = Observable.FromAsyncPattern<byte[], int, int, int>(source.BeginRead, source.EndRead);
            Buffer = new byte[bufferSize];
        }

        public IObservable<int> ReadNext()
        {
            return _factory(Buffer, 0, _bufferSize);
        }

        public byte[] Buffer { get; set; }
    }
}

然后还需要将源类型更改为NetworkStream

我认为我的代码中的调度可以帮助您解决阻塞问题。另一种选择是,如果合适的话(我仍然不太理解您的问题),您可以使用 .切换并创建嵌套的可观察对象

这意味着当一些数据通过时,你会把它全部读出来,直到它完成,然后再完成。一旦你完成,你会开始另一个序列,这将是任何进一步的数据

s1 --1-0-1-1|
s2          ---1-0-0-1-|        
s3                     ---0-0-1-0-1|
etc..
out--1-0-1-1---1-0-0-1----0-0-1-0-1|
s1、s2、s3等是在stream.DataAvailable之前的数据突发序列。然后这些内部流将完成,并启动一个请求(创建另一个内部可观察序列s2、s3、sN)。切换(或合并或Concat)都将能够将这些多个序列展平为一个序列,供用户使用

另一种可能更容易编码的替代方法是使用IEnumerable

public IEnumerable<IObservable<byte>> ConstantRead(string path)
{
    while (true)
    {
        yield return Observable.Using(
                () => new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.None),
                stream => stream.ToObservable(4096, Scheduler.ThreadPool));
    }
}
_subscription = ConstantRead(@"C:\Users\Lee\MyFile.zip")
                .Concat()
                .Subscribe(...
我希望这有帮助


Lee Campbell

当没有可用数据(尚未)时,您想做什么?等待直到有可用数据或停止?当有可用数据可读取时,否则将完成。接受数据块的外部观察者再次轮询流以获取可用数据,但不包括上述部分(AsyncRead)如果没有数据可用,则需要在不发出AsyncReadChunk的情况下完成。我不确定问题是否出在代码上。因为在最初的60秒之后。即使没有可用数据,也会根据需要阻止下一次读取。这是因为您使用的是Repeat.Repeat,无限期重复,因此AsyncReadChunk方法基本上是调用在一个无限循环中读取数据。您想要哪种行为?您打算从流中读取所有数据吗?@SametSorgut-这可能是由于超时造成的。我想以块的形式读取数据,直到没有可用的数据。我的AsyncRead使用AsyncReadChunk读取bufferSize中的数据块,并检查是否仍有一些可用的数据。如果是,keeps读取。当没有可用数据时,它将完成。外部观察者在收到某些数据时再次调用AsyncRead。这将一直持续到用户断开连接。我添加了轮询代码。它收集所有读取块并形成有效负载。。。
s1 --1-0-1-1|
s2          ---1-0-0-1-|        
s3                     ---0-0-1-0-1|
etc..
out--1-0-1-1---1-0-0-1----0-0-1-0-1|
public IEnumerable<IObservable<byte>> ConstantRead(string path)
{
    while (true)
    {
        yield return Observable.Using(
                () => new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.None),
                stream => stream.ToObservable(4096, Scheduler.ThreadPool));
    }
}
_subscription = ConstantRead(@"C:\Users\Lee\MyFile.zip")
                .Concat()
                .Subscribe(...