C#:实现 NetworkStream.Peek?
目前,C# 中没有 NetworkStream.Peek
方法。实现这种方法的最佳方法是什么,该方法的功能与 NetworkStream.ReadByte 类似,只是返回的字节实际上并未从 Stream 中删除?
Currently, there isn't a NetworkStream.Peek
method in C#. What is the best way of implementing such a method which functions just like NetworkStream.ReadByte
except that the returned byte
is not actually removed from the Stream
?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
我遇到了同样的“查看幻数,然后决定将流发送到哪个流处理器”的要求,不幸的是,我无法通过传递已经消耗的字节来解决这个问题 - 正如对 Aaronaught 答案的评论中所建议的那样在单独的参数中进入流处理方法,因为这些方法是给定的,它们期望 System.IO.Stream 而不是其他任何东西。
我通过创建一个或多或少通用的包装 Stream 的 PeekableStream 类来解决这个问题。它适用于 NetworkStreams,也适用于任何其他 Stream,前提是您使用 Stream.CanRead。
编辑
或者,您可以使用全新的
ReadSeekableStream
并执行无论如何,
PeekableStream
来了:I ran into the same 'peek for magic number and then decide which stream processor to send the stream to' requirement and unfortunately can't weasel my way out of that problem - as suggested in comments to Aaronaught's answer - by passing the already consumed bytes into the stream processing methods in separate parameters, as those methods are a given and they expect System.IO.Stream and nothing else.
I solved this by creating a more or less universal PeekableStream class that wraps a Stream. It works for NetworkStreams, but also for any other Stream, provided you Stream.CanRead it.
Edit
Alternatively, you could use the brand new
ReadSeekableStream
and doIn any event, here comes
PeekableStream
:如果您不需要实际检索字节,可以参考
DataAvailable
属性。否则,您可以使用
StreamReader
并调用其
Peek
方法。请注意,由于延迟问题,这两种方法对于从网络流中读取数据都不是特别可靠。数据可能在您查看之后立即变得可用(存在于读取缓冲区中)。
我不确定您打算用它做什么,但是
NetworkStream
上的Read
方法是一个阻塞调用,因此您实际上不需要检查状态,即使您收到的是大块。如果您试图在从流读取时保持应用程序响应,则应使用线程或异步调用来接收数据。编辑:根据这篇文章,
StreamReader.Peek
是NetworkStream 上存在错误,或者至少具有未记录的行为,因此如果您选择走这条路,请务必小心。已更新 - 对评论的回复
“查看”实际流本身的概念实际上是不可能的;它只是一个流,一旦接收到字节,它就不再在流上。有些流支持查找,因此您可以从技术上重新读取该字节,但 NetworkStream 不是其中之一。
窥视仅适用于将流读入缓冲区时;一旦数据位于缓冲区中,那么查看就很容易,因为您只需检查缓冲区中当前位置的任何内容。这就是为什么 StreamReader 能够做到这一点;通常,
Stream
类不会有自己的Peek
方法。现在,具体针对这个问题,我质疑这是否真的是正确的答案。我理解动态选择处理流的方法的想法,但是您真的需要在原始流上执行此操作吗?您不能先将流读入字节数组,甚至将其复制到 MemoryStream 中,然后从该点开始处理它吗?
我看到的主要问题是,如果当您从网络流中读取数据时发生问题,数据就会消失。但如果您先将其读入临时位置,则可以对其进行调试。您可以找出数据是什么以及尝试处理数据的对象中途失败的原因。
一般来说,您想要使用 NetworkStream 做的第一件事就是将其读入本地缓冲区。我能想到不这样做的唯一原因是,如果您正在读取大量数据 - 即使这样,如果内存不适合文件系统,我可能会考虑使用文件系统作为中间缓冲区。
我不知道您的确切要求,但从目前为止我所了解到的情况来看,我的建议是:不要尝试直接从
NetworkStream
处理数据,除非有令人信服的理由这样做。考虑首先将数据读入内存或磁盘,然后处理副本。If you don't need to actually retrieve the byte, you can refer to the
DataAvailable
property.Otherwise, you can wrap it with a
StreamReader
and invoke itsPeek
method.Note that neither of these are particularly reliable for reading from a network stream, due to latency issues. The data might become available (present in the read buffer) the very instant after you peek.
I'm not sure what it is that you intend to do with this, but the
Read
method onNetworkStream
is a blocking call, so you don't really need to check for status, even if you are receiving in chunks. If you are trying to keep the application responsive while reading from the stream, you should use a thread or asynchronous call to receive the data instead.Edit: According to this post,
StreamReader.Peek
is buggy on aNetworkStream
, or at least has undocumented behaviour, so be careful if you choose to go that route.Updated - response to comments
The notion of a "peek" on the actual stream itself is actually impossible; it's just a stream, and once the byte is received then it is no longer on the stream. Some streams support seeking so you could technically re-read that byte, but
NetworkStream
isn't one of them.Peeking only applies when are reading the stream into a buffer; once the data is in a buffer then peeking is easy because you just check whatever's at the current position in the buffer. That's why a
StreamReader
is able to do this; noStream
class will generally have its ownPeek
method.Now, for this problem specifically, I question whether or not this is really the right answer. I understand the idea of dynamically selecting a method for processing the stream, but do you really need to do this on the raw stream? Can you not read the stream into a byte array first, or even copy it into a
MemoryStream
, and process it from that point on?The main issue I see is that if something bad happens when you're reading from a network stream, the data is gone. But if you read it into a temporary location first, you can debug this. You can find out what the data was and why the object that was trying to process the data failed halfway through.
In general, the very first thing you want to do with a
NetworkStream
is read it into a local buffer. The only reason I can think of not to do this is if you're reading an enormous amount of data - and even then, I might consider using the file system as an intermediate buffer if it won't fit in memory.I don't know your exact requirements, but from what I've learned so far, my advice would be: Don't try to process your data directly from the
NetworkStream
unless there is a compelling reason to do so. Consider reading the data into memory or onto disk first, then processing the copy.如果您有权访问 Socket 对象,则可以尝试 接收方法,传递
SocketFlags.Peek
。这类似于可以传递给 BSD 套接字或 Winsock 中的recv
调用的MSG_PEEK
标志。If you have access to the
Socket
object, you could try the Receive method, passingSocketFlags.Peek
. This is analogous to theMSG_PEEK
flag that can be passed to therecv
call in BSD Sockets or Winsock.这是一个非常简单的
PeekStream
实现,它允许您仅在流的开头查看一定数量的字节(而不是随时查看)。所查看的字节本身作为Stream
返回,以最大限度地减少对现有代码的更改。使用方法如下:
GetInitialBytesStream()
返回一个可查找流,其中最多包含基础流的peekSize
个初始字节(如果流短于peekSize,则该字节数会更少) )。
由于其简单性,读取 PeekStream 应该只比直接读取底层流稍微慢一点(如果有的话)。
免责声明:上面的 PeekStream 取自一个工作程序,但没有经过全面测试,因此可能包含错误。它对我有用,但您可能会发现一些失败的极端情况。
Here is a very simple
PeekStream
implementation that allows you to peek a certain number of bytes at the start of the stream only (as opposed to being able to peek at any time). The peeked bytes are returned as aStream
themselves, to minimize changes to existing code.Here's how you use it:
GetInitialBytesStream()
returns a seekable stream that contains up topeekSize
initial bytes of the underlying stream (less if the stream is shorter thanpeekSize
).Because of its simplicity, reading PeekStream should only be marginally slower (if at all) than reading underlying stream directly.
Disclaimer: PeekStream above is taken from a working program, but it's not comprehensively tested, so may contain bugs. It works for me, but you might uncover some corner cases where is fails.
FWIW,这是一个与不可查找流相比的可查看流,仅针对前面一个字节进行了优化:
FWIW, here is a peekable stream over a non-seekable one, optimized for just one byte ahead: