当前位置: 首页 > 编程语言 > C#

实现NetworkStream.Peek?分享

时间:2023-04-11 02:37:24 C#

C#:实现NetworkStream.Peek?目前,C#中没有NetworkStream.Peek方法。实现类似NetworkStream.ReadByte的函数的最佳方法是什么,除了返回的字节实际上并未从流中删除?如果不需要实际检索字节,可以参考DataAvailable属性。否则,您可以用StreamReader包装它并调用它的Peek方法。请注意,由于延迟问题,这些对于从网络流中读取都不是特别可靠。在您查看之后,数据可能会变得可用(存在于读取缓冲区中)。我不确定你打算用它做什么,但是NetworkStream上的Read方法是一个阻塞调用,所以即使你正在接收块,你也不需要检查状态。如果您试图在从流读取时保持应用程序响应,则应使用线程或异步调用来接收数据。编辑:根据这篇文章,NetworkStream上的StreamReader.Peek有问题,或者至少有未记录的行为,所以如果您选择走那条路,请小心。更新——对评论的回应“偷看”实际流本身的概念是不可能的;它只是一个流,一旦接收到字节,它就不再在流中了。一些流支持查找,因此您可以在技术上重新读取该字节,但NetworkStream不是其中之一。窥视仅在流被读入缓冲区时起作用;一旦数据在缓冲区中,那么查看就很容易了,因为您只需要检查当前位置的缓冲区内容。这就是StreamReader能够做到这一点的原因;没有Stream类通常会有自己的Peek方法。现在,特别是对于这个问题,我怀疑这实际上是正确的答案。我理解动态选择处理流的方法的想法,但你真的需要在原始流上这样做吗?您能否先将流读入字节数组,甚至将其复制到MemoryStream,然后从那时起对其进行处理?我看到的主要问题是,如果从网络流中读取时发生错误,数据就会消失。但是,如果先将其读入临时位置,则可以对其进行调试。您可以找出数据是什么以及为什么试图处理它的对象中途失败。通常,您想要对NetworkStream做的第一件事是将其读入本地缓冲区。我能想到不这样做的唯一原因是,如果你正在读取大量数据——即便如此,如果它不适合内存,我可能会考虑使用文件系统作为中间缓冲区。我不知道您的确切要求,但根据我目前所了解的情况,我的建议是:不要尝试直接从NetworkStream处理您的数据,除非有令人信服的理由这样做。考虑先将数据读入内存或磁盘,然后再处理副本。我遇到了同样的“偷看幻数,然后决定将流发送到”所需的流处理器,不幸的是,我无法摆脱它——正如对Aaronaught的回答的评论中所建议的那样——通过已经consumed字节以单独的参数进入流处理方法,因为这些方法被赋予它们期望System.IO.Stream而没有其他。我通过创建一个包含Stream的或多或少通用的PeekableStream类解决了这个问题。它适用于NetworkStreams,但也适用于任何其他Stream,只要您Stream.CanRead即可。编辑或者你可以使用全新的ReadSeekableStream来做varreadSeekableStream=newReadSeekableStream(networkStream,/*>=*/count);...readSeekableStream.Read(...,count);readSeekableStream.Seek(-count,SeekOrigin.Current);无论如何,PeekableStream://////PeekableStream包装了一个Stream并可用于在底层流中提前查看,///而无需消耗字节。换句话说,执行Peek()将允许您在流中向前看,///但它不会影响后续Read()调用的结果。//////这有时是必要的,例如,为了查看字节流的幻数并决定哪个///流处理器移交该流。///publicclassPeekableStream:Stream{privatereadonlyStreamunderlyingStream;私有只读字节[]lookAheadBuffer;私有intlookAheadIndex;publicPeekableStream(StreamunderlyingStream,intmaxPeekBytes){this.underlyingStream=underlyingStream;lookAheadBuffer=新字节[maxPeekBytes];}protectedoverridevoidDispose(booldisposing){if(disposing)underlyingStream.Dispose();base.Dispose(处置);}//////查看最多count个字节,如果流在可以读取该字节数之前结束,则查看更少。//////对此方法的调用不会影响对Read()和Peek()的后续调用。//////请注意,此方法将始终查看计数字节,除非在此之前到达流的末尾-与Read()///方法相反,后者可能读取少于计数字节,即使尚未到达流的末尾。//////字节数组。当此方法返回时,缓冲区包含指定的字节数组,偏移量和///(偏移量+已查看字节数-1)之间的值将替换为从当前源中查看的字节数。///缓冲区中从零开始的字节偏移量,从该偏移量开始存储从当前流中窥视的数据。///要查看的最大字节数来自当前流。///查看缓冲区的总字节数。如果它小于请求的字节数,则已到达流的末尾。publicvirtualintPeek(byte[]buffer,intoffset,intcount){if(count>lookAheadBuffer.Length)thrownewArgumentOutOfRangeException("count","必须小于可窥视的大小,即"+lookAheadBuffer.Length);while(lookAheadIndex0&&lookAheadIndex>0){bytesTakenFromLookAheadBuffer=Math.Min(count,lookAheadIndex);Array.Copy(lookAheadBuffer,0,buffer,offset,bytesTakenFromLookAheadBuffer);count-=bytesTakenFromLookAheadBuffer;偏移量+=bytesTakenFromLookAheadBuffer;lookAheadIndex-=bytesTakenFromLookAheadBuffer;if(lookAheadIndex>0)//根据http://msdn.microsoft.com/en-us/library/z50k9bft(v=VS.90).aspx://"如果sourceArray和destinationArray重叠,此方法的行为就好像sourceArray的原始值//在destinationArray被覆盖之前的临时位置中被保留。"Array.Copy(lookAheadBuffer,lookAheadBuffer.Length-bytesTakenFromLookAheadBuffer+1,lookAheadBuffer,0,lookAheadIndex);}return计数>0?bytesTakenFromLookAheadBuffer+underlyingStream.Read(buffer,offset,count):bytesTakenFromLookAheadBuffer;}publicoverrideintReadByte(){if(lookAheadIndex>0){lookAheadIndex--;bytefirstByte=lookAheadBuffer[0];if(lookAheadIndex>0)//将lookAheadBuffer中剩余的字节移到前面Array.Copy(lookAheadBuffer,1,lookAheadBuffer,0,lookAheadIndex);returnfirstByte;}else{returnunderlyingStream.ReadByte();}}publicoverridelongSeek(longoffset,SeekOriginorigin){longret=underlyingStream.Seek(offset,origin);lookAheadIndex=0;//这需要在调用underlyingStream.Seek()之后完成,因为这可能会抛出NotSupportedException,//在这种情况下我们不想更改lookAhead状态returnret;}//从这里开始,只有对underlyingStream的简单委托publicoverrideboolCanSeek{get{returnunderlyingStream.CanSeek;}}publicoverrideboolCanWrite{get{returnunderlyingStream.CanWrite;}}publicoverrideboolCanTimeout{get{returnunderlyingStream.CanTimeout;}}publicoverrideintReadTimeout{get{returnunderlyingStream.ReadTimeout;}set{underlyingStream.ReadTimeout=value;}}publicoverrideintWriteTimeout{get{returnunderlyingStream.WriteTimeout;}set{underlyingStream.WriteTimeout=value;}}publicoverridevoidFlush(){underlyingStream.Flush();}publicoverridelongLength{get{returnunderlyingStream.Length;}}publicoverridevoidSetLength(longvalue){underlyingStream.SetLength(value);}publicoverridevoidWrite(byte[]buffer,intoffset,intcount){underlyingStream.Write(buffer,offset,数数);}publicoverridevoidWriteByte(bytevalue){underlyingStream.WriteByte(value);如果您有权访问Socket对象,则可以尝试Receive方法,传递SocketFlags.Peek这类似于可以传递给BSDMSG_PEEK标志以在套接字或Winsock上进行recv调用。这是PeekStream的一个非常简单的实现,它允许您只查看流开头的特定数量的字节(而不是能够随时查看)。隐藏字节作为Stream本身返回,以尽量减少对现有代码的更改。下面是你如何使用它:StreamnonSeekableStream=...;PeekStreampeekStream=newPeekStream(nonSeekableStream,30);//Peekmax30bytesStreaminitialBytesStream=peekStream.GetInitialBytesStream();ParseHeaders(initialBytesStream);//处理nonSeekableStream的初始字节peekStream.Read(...)//正常读取,读取将从头开始比peekSize短,然后是peekSize)。由于它的简单性,读取PeekStream应该只比直接读取底层流稍微慢一点(如果有的话)。公共类PeekStream:Stream{privateStreamm_stream;私有字节[]m_buffer;私人诠释m_start;私人诠释m_end;publicPeekStream(Streamstream,intpeekSize){if(stream==null){thrownewArgumentNullException("stream");}if(!stream.CanRead){thrownewArgumentException("流不可读。");}if(peekSizebuffer.Length){thrownewArgumentOutOfRangeException("count");}inttotalRead=0;//从缓冲区读取if(m_start0){totalRead+=m_stream.Read(buffer,offset,count);}//返回读取的总字节数returntotalRead;}publicoverridevoidWrite(byte[]buffer,intoffset,intcount){thrownewNotImplementedException();}publicoverrideintReadByte(){if(m_start免责声明:上面的PeekStream取自一个工作程序,但它没有经过彻底测试,因此可能包含错误。它对我有用,但你可能会发现一些极端的失败案例以上就是C#学习教程:C#:RealizingNetworkStream.Peek?的所有内容分享,如果对大家有用,需要详细了解C#学习教程,希望大家多多关注---这篇文章整理自网络,不代表立场,如涉及侵权,请点右联系管理员删除。如需转载请注明出处: