我认为BinaryReader.ReadChars()方法出现了问题。当我偶尔将BinaryReader包装在原始套接字NetworkStream上时,就会发现流损坏,其中正在读取的流不同步。所讨论的流包含二进制序列化协议中的消息。
我追踪到以下几个方面
我认为正在发生的事情如下(在下面的例子中)
我认为根是.NET代码中的一个bug,它使用charsRemaining *字节/char每个循环来计算所需的剩余字节。由于解码器中隐藏的额外字节,此计算可以由一个字节关闭,从而在输入流之外消耗额外的字节。
下面是有问题的.NET框架代码
while (charsRemaining>0) {
// We really want to know what the minimum number of bytes per char
// is for our encoding. Otherwise for UnicodeEncoding we'd have to
// do ~1+log(n) reads to read n characters.
numBytes = charsRemaining;
if (m_2BytesPerChar)
numBytes <<= 1;
numBytes = m_stream.Read(m_charBytes, 0, numBytes);
if (numBytes==0) {
return (count - charsRemaining);
}
charsRead = m_decoder.GetChars(m_charBytes, 0, numBytes, buffer, index);
charsRemaining -= charsRead;
index+=charsRead;
}我不完全确定这是一个错误,还是只是对API的误用。为了解决这个问题,我只需要自己计算所需的字节,读取它们,然后通过相关的byte[] ()运行Encoding.GetString()。然而,对于像UTF-8这样的东西,这是行不通的。
想听听人们对这件事的看法,以及我是否做错了什么。也许它将为下一个人节省几个小时/几天乏味的调试。
编辑:发布以连接连接跟踪项
发布于 2009-11-26 16:36:12
我重复了你在BinaryReader.ReadChars中提到的问题。
虽然开发人员在合成诸如流和解码器之类的东西时总是需要考虑前瞻,但在BinaryReader中这似乎是一个相当重要的错误,因为该类的目的是读取由各种类型的数据组成的数据结构。在这种情况下,我同意ReadChars在读取内容时应该更加保守,以避免丢失该字节。
直接使用Decoder没有什么问题,毕竟这就是ReadChars在幕后所做的事情。
Unicode是一个简单的例子。如果考虑到任意编码,实际上没有通用的方法来确保在传入字符计数而不是字节计数时消耗正确的字节数(考虑不同长度的字符和涉及格式错误的输入的情况)。因此,避免使用BinaryReader.ReadChars来读取特定字节数提供了一个更健壮、更通用的解决方案。
我建议您通过http://connect.microsoft.com/visualstudio将此提请微软注意。
发布于 2009-11-26 16:34:05
有趣;您可以在“连接”上报告这一点。作为止损间隙,您也可以尝试使用BufferredStream包装,但我认为这是在掩盖裂缝(这种情况仍可能发生,但频率较低)。
当然,另一种方法是预先缓冲整个消息(但不是整个流);然后从类似MemoryStream的内容中读取--假设您的网络协议具有逻辑消息(最好是长度前缀,并且不太大)。然后,当它解码所有的数据是可用的。
发布于 2009-11-26 17:32:22
这让人想起了我自己的一个问题(从HttpResponseStream读取失败),在这里,我遇到了一个问题,当从HTTP响应流读取时,StreamReader会认为它过早地到达了流的末尾,所以我的解析器会意外地崩溃。
正如Marc对您的问题提出的建议,我首先尝试了在MemoryStream中进行预缓冲,这很好,但这意味着如果您有一个大文件要读取(特别是从网络/web),那么您可能需要等待很长时间才能对其进行任何有用的操作。我最终决定创建我自己的TextReader扩展,它覆盖读取方法并使用ReadBlock方法定义它们(该方法执行阻塞读取,即它等待直到得到所要求的字符数)。
您的问题可能与我的问题一样,因为Read方法无法保证返回您所要求的字符数,例如,如果您查看BinaryReader.Read (http://msdn.microsoft.com/en-us/library/ms143295.aspx)方法的文档,您将看到它声明:
返回值 类型: System..::.Int32 读入缓冲区的字符数。如果没有可用的字节数,这可能会少于请求的字节数,或者如果到达流的末尾,则可能为零。
由于BinaryReader没有像TextReader这样的ReadBlock方法,所以您所能做的就是采取自己的方法来监视自己或Marc的预缓存位置。
https://stackoverflow.com/questions/1804433
复制相似问题