当流中没有数据时,我尝试读取流块60秒。 当有一些数据时,读取按需要完成。 如何重写以下代码,以便只有在stream.DataAvailable为true时才能读取?
我想我需要类似Observable.While(dataAvailableObserver,AsyncRead)..
public static IObservable<byte[]> AsyncRead(this NetworkStream stream, int bufferSize)
{
return Observable.Create<byte[]>(
o => Observable.Defer(() => AsyncReadChunk(stream, bufferSize))
.Repeat()
.Subscribe(dataChunk =>
{
if (dataChunk.Length > 0)
{
o.OnNext(dataChunk);
return;
}
Debug.Assert(!stream.DataAvailable);
o.OnCompleted();
}, o.OnError, o.OnCompleted));
}
public static IObservable<byte[]> AsyncReadChunk(this NetworkStream stream, int bufferSize)
{
var buffer = new byte[bufferSize];
return Observable.FromAsyncPattern<byte[], int, int, int>(stream.BeginRead, stream.EndRead)(buffer, 0, bufferSize)
.Select(cbRead =>
{
Console.WriteLine("Data chunk received.");
var dataChunk = new byte[cbRead];
Buffer.BlockCopy(buffer, 0, dataChunk, 0, cbRead);
return dataChunk;
});
}
我发现读取小的bufferSizes,因为较大的缓冲区导致缓冲区的等待被填充(就像在我的情况下,传入的数据是小数据包)。
答案 0 :(得分:0)
由于您使用的是Defer,您必须检查延迟逻辑中的可用数据。最简单的方法是在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 =>
答案 1 :(得分:0)
我不确定这是否有用,但这是我用来读取流的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,但听起来你可以交换支票
if (bytesRead > 0)
到
if (source.DataAvailable)
您还需要将源类型更改为NetworkStream。
我认为我的代码中的调度可能会帮助您解决阻塞问题。如果合适的另一种选择(我仍然不太了解你的问题),你可以使用 。切换并创建一个嵌套的observable。
这意味着当一些数据通过时你会全部读完,直到完成然后完成。完成后,启动另一个序列,这将是任何进一步的数据。
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)。 Switch(或Merge或Concat)都可以将这些多个序列展平为一个序列,供用户使用。
另一种可能更容易编码的替代方案是拥有IEnumerable&lt; IObservable&lt; byte&gt;&gt;。只需像这样的方法即可轻松创建这些
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(...
我希望有所帮助。
李坎贝尔