简体   繁体   English

从NetworkStream读取会损坏缓冲区

[英]Reading from NetworkStream corrupts the buffer

When reading data from NetworkStream with ReadUntilClosedObservable1, the returned data is corrupted like some blocks of read data overlap. 当使用ReadUntilClosedObservable1从NetworkStream读取数据时,返回的数据将被破坏,就像某些读取数据块重叠一样。

However, when I read the data with ReadUntilClosedObservable2 the data arrives without problems. 但是,当我使用ReadUntilClosedObservable2读取数据时,数据到达就没有问题。

I want to use the ReadUntilClosedObservable1 because repeatedly reading from stream in ReadUntilClosedObservable2 is burning the CPU. 我想使用ReadUntilClosedObservable1,因为从ReadUntilClosedObservable2中的流中反复读取正在燃烧CPU。

How can I get the messages in sync order? 如何获得同步消息?

UPDATE: 更新:

return Observable.Timer(TimeSpan.Zero, interval, TaskPoolScheduler.Default)
                    .SelectMany(_ => readToEnd)
                    .Where(dataChunk => dataChunk.Length > 0);

I just noticed that readToEnd to is fired again and again before it finishes the previous job. 我只是注意到readToEnd to在完成之前的工作之前被一再触发。 Doesn't it need to be synchronized? 不需要同步吗? If Observable.Timer is the problem how can I achieve the same effect without it, reading in intervals but starting without waiting? 如果出现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);
}

Oh, no, no...don't do it like that... 哦,不,不...不要那样做...

Async + Rx is one of the more... non-intuitive setups to get working, but it is quite a bit simpler than what you're attempting. Async + Rx是更多... 非直观的设置之一,但是比您尝试的要简单得多。 The key bits are three different Rx operators: 关键位是三种不同的Rx运算符:

  • FromAsyncPattern : generates a " IObservable Factory" from an async call signature FromAsyncPattern :从异步调用签名生成“ IObservable Factory”
  • Observable.Defer : allows you to use above IObservable factory to generate observables per subscriber Observable.Defer :允许您使用以上IObservable工厂为每个订阅者生成可观察值
  • Observable.While : allows you to "Reinvoke until I say when" on an IObservable Observable.While :允许您在IObservable上“重新调用,直到我说何时”

(EDIT: altered to use a NetworkStream example) (编辑:更改为使用NetworkStream示例)

(doubleEDIT: altered based on comments) (doubleEDIT:根据评论进行了更改)

Try this - unless I miss my guess, it's more or less what you're trying for: 尝试一下-除非我错过我的猜测,否则或多或少是您想要的目标:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM