当使用ReadUntilClosedObservable1从NetworkStream读取数据时,返回的数据被破坏,就像一些读取数据块重叠一样。
但是,当我使用ReadUntilClosedObservable2读取数据时,数据会毫无问题地到达。
我想使用ReadUntilClosedObservable1,因为在ReadUntilClosedObservable2中重复读取流正在刻录CPU。
如何以同步顺序获取消息?
更新
return Observable.Timer(TimeSpan.Zero, interval, TaskPoolScheduler.Default)
.SelectMany(_ => readToEnd)
.Where(dataChunk => dataChunk.Length > 0);
我刚刚注意到readToEnd
在完成上一个作业之前一次又一次地被触发。它不需要同步吗?如果Observable.Timer
是问题,如果没有它,我怎么能达到相同的效果,按间隔阅读,但不等待开始?
public static IObservable<int> ReadObservable(this Stream stream, byte[] buffer
,int offset, int count)
{
return stream.ReadAsync(buffer, offset, count)
.ToObservable();
}
public static IObservable<byte[]> ReadObservable(this Stream stream,
int bufferSize)
{
var buffer = new byte[bufferSize];
return stream.ReadObservable(buffer, 0, buffer.Length)
.Select(cbRead =>
{
if (cbRead == 0)
{
return new byte[0];
}
if (cbRead == buffer.Length)
{
return buffer;
}
var dataChunk = new byte[cbRead];
Buffer.BlockCopy(buffer, 0, dataChunk,
0, cbRead);
return dataChunk;
});
}
public static IObservable<byte[]> ReadUntilClosedObservable1(this NetworkStream
stream, int bufferSize, TimeSpan interval)
{
var readToEnd = Observable.Defer(() => stream.ReadObservable(bufferSize))
.DoWhile(() => stream.DataAvailable)
.ToList()
.Select(dataChunks =>
{
var buffer = new List<byte>();
foreach (var dataChunk in dataChunks)
{
buffer.AddRange(dataChunk);
}
return buffer.ToArray();
});
return Observable.Timer(TimeSpan.Zero, interval, TaskPoolScheduler.Default)
.SelectMany(_ => readToEnd)
.Where(dataChunk => dataChunk.Length > 0);
}
public static IObservable<byte[]> ReadUntilClosedObservable2(this Stream stream
,int bufferSize)
{
return Observable.Defer(() => stream.ReadObservable(bufferSize))
.Repeat()
.Where(dataChunk => dataChunk.Length > 0);
}
答案 0 :(得分:0)
哦,不,不......不要这样做......
Async + Rx是更多...... 非直观的设置之一,但它比你尝试的更简单。关键位是三个不同的Rx运算符:
FromAsyncPattern
:生成&#34; IObservable
工厂&#34;来自异步呼叫签名Observable.Defer
:允许您使用上述IObservable
工厂为每个订阅者生成可观察项目Observable.While
:允许你重新启动,直到我说&#34;在IObservable
(编辑:改为使用NetworkStream
示例)
(doubleEDIT:根据评论改变)
试试这个 - 除非我错过了我的猜测,否则它或多或少都是你想要的:
void Main()
{
// We'll feed this to listener
var message = "Yo mamma said you like messages like this";
var listenerTask = Task
.Factory
.StartNew(() =>
{
var bufferSize = 1024;
var localhost = new IPAddress(new byte[]{127,0,0,1});
var listener = new TcpListener(localhost, 11201);
listener.Start();
var incomingClient = listener.AcceptTcpClient();
var clientStream = incomingClient.GetStream();
// our buffered reader
var observer = clientStream.ReadObservable(bufferSize);
var compareBuffer = observer
// Take while we're getting data and the client
// is still connected
.TakeWhile(returnBuffer => returnBuffer.Length > 0 &&
incomingClient.Connected)
// In between read blocks, respond back to the client
// No need for fanciness here, just normal async writeback
.Do(returnBuffer => clientStream.BeginWrite(
returnBuffer,
0,
returnBuffer.Length,
ar => clientStream.EndWrite(ar),
null))
.ToEnumerable()
.SelectMany (returnBuffer => returnBuffer)
.ToArray();
listener.Stop();
Console.WriteLine(
"Listener thinks it was told... {0}",
Encoding.ASCII.GetString(compareBuffer));
});
var clientTask = Task.Factory.StartNew(
() =>
{
var client = new TcpClient();
client.Connect("localhost", 11201);
var random = new Random();
var outStream = client.GetStream();
var bytesToSend = Encoding.ASCII.GetBytes(message);
foreach(byte toSend in bytesToSend)
{
// send a character over
outStream.WriteByte(toSend);
// Listener should parrot us...
int goOn = outStream.ReadByte();
if(goOn != toSend)
{
Console.WriteLine(
"Huh. Listener echoed wrong. I said: {0}, they said {1}",
toSend,
goOn);
break;
}
Console.WriteLine("I said: {0}, they said {1}", toSend, goOn);
// Take a little nap (simulate latency, etc)
Thread.Sleep(random.Next(200));
}
client.Close();
});
Task.WaitAll(listenerTask, clientTask);
}
public static class Ext
{
public static IObservable<byte[]> ReadObservable(this Stream stream, int bufferSize)
{
// to hold read data
var buffer = new byte[bufferSize];
// Step 1: async signature => observable factory
var asyncRead = Observable.FromAsyncPattern<byte[], int, int, int>(
stream.BeginRead,
stream.EndRead);
return Observable.While(
// while there is data to be read
() => stream.CanRead,
// iteratively invoke the observable factory, which will
// "recreate" it such that it will start from the current
// stream position - hence "0" for offset
Observable.Defer(() => asyncRead(buffer, 0, bufferSize))
.Select(readBytes => buffer.Take(readBytes).ToArray()));
}
}