首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >BinaryReader.ReadChars()问题

BinaryReader.ReadChars()问题
EN

Stack Overflow用户
提问于 2009-11-26 15:48:06
回答 4查看 4.1K关注 0票数 7

我认为BinaryReader.ReadChars()方法出现了问题。当我偶尔将BinaryReader包装在原始套接字NetworkStream上时,就会发现流损坏,其中正在读取的流不同步。所讨论的流包含二进制序列化协议中的消息。

我追踪到以下几个方面

  • 只有在读取unicode字符串(使用Encoding.BigEndian编码)时才会发生这种情况。
  • 只有当所讨论的字符串被分成两个tcp数据包(使用wireshark确认)时,才会发生这种情况。

我认为正在发生的事情如下(在下面的例子中)

  • BinaryReader.ReadChars()被调用,要求它读取3个字符(字符串长度在字符串本身之前被编码)
  • 第一个循环内部请求从网络流中读取6个字节(3个剩余字符*2个字节/char)
  • 网络流只有3个字节可用。
  • 3个字节读入本地缓冲区
  • 发送给解码器的缓冲区
  • 解码器解码一个字符,并将另一个字节保存在它自己的内部缓冲区中
  • 第二个循环内部请求读取4个字节!(2个剩余字符*2个字节/char)
  • 网络流有全部4个字节可用。
  • 4个字节读入本地缓冲区
  • 发送给解码器的缓冲区
  • 解码器解码2个字符,并在内部保留剩余的第4个字节。
  • 字符串解码已完成。
  • 序列化代码试图解除对下一项的编组,并由于流的损坏而发出嘎嘎声。 char[]缓冲器=新char3;int charIndex = 0;解码器= Encoding.BigEndianUnicode.GetDecoder();//假想6个字节中的3个到达一个包byte[] b1 =新byte[] { 0,83,0 };int charsRead = decoder.GetChars(b1,0,3,buffer,charIndex);charIndex += charsRead;//假装其余3个字节加上最后一个字节,用于无关的东西,//到达下一个byte[] b2 = new byte[] { 71,0,114,3 };charsRead = decoder.GetChars(b2,0,4,缓冲器,charIndex);charIndex += charsRead;

我认为根是.NET代码中的一个bug,它使用charsRemaining *字节/char每个循环来计算所需的剩余字节。由于解码器中隐藏的额外字节,此计算可以由一个字节关闭,从而在输入流之外消耗额外的字节。

下面是有问题的.NET框架代码

代码语言:javascript
复制
    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这样的东西,这是行不通的。

想听听人们对这件事的看法,以及我是否做错了什么。也许它将为下一个人节省几个小时/几天乏味的调试。

编辑:发布以连接连接跟踪项

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2009-11-26 16:36:12

我重复了你在BinaryReader.ReadChars中提到的问题。

虽然开发人员在合成诸如流和解码器之类的东西时总是需要考虑前瞻,但在BinaryReader中这似乎是一个相当重要的错误,因为该类的目的是读取由各种类型的数据组成的数据结构。在这种情况下,我同意ReadChars在读取内容时应该更加保守,以避免丢失该字节。

直接使用Decoder没有什么问题,毕竟这就是ReadChars在幕后所做的事情。

Unicode是一个简单的例子。如果考虑到任意编码,实际上没有通用的方法来确保在传入字符计数而不是字节计数时消耗正确的字节数(考虑不同长度的字符和涉及格式错误的输入的情况)。因此,避免使用BinaryReader.ReadChars来读取特定字节数提供了一个更健壮、更通用的解决方案。

我建议您通过http://connect.microsoft.com/visualstudio将此提请微软注意。

票数 3
EN

Stack Overflow用户

发布于 2009-11-26 16:34:05

有趣;您可以在“连接”上报告这一点。作为止损间隙,您也可以尝试使用BufferredStream包装,但我认为这是在掩盖裂缝(这种情况仍可能发生,但频率较低)。

当然,另一种方法是预先缓冲整个消息(但不是整个流);然后从类似MemoryStream的内容中读取--假设您的网络协议具有逻辑消息(最好是长度前缀,并且不太大)。然后,当它解码所有的数据是可用的。

票数 1
EN

Stack Overflow用户

发布于 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的预缓存位置。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/1804433

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档