C# 异步的。从NetworkStream块读取60秒。(Rx)
当流中没有数据时,我尝试读取流块60秒。 当存在一些数据时,读取将按需要完成。 如何重写以下代码,使其在stream.DataAvailable为true时只能读取 我想我需要像Observable.While(dataAvailableObserver,AsyncRead)这样的东西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
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(...