我遇到了我认为 BinaryReader.ReadChars() 方法的问题。当我在原始套接字 NetworkStream 周围包装 BinaryReader 时,偶尔会出现流损坏,其中正在读取的流不同步。有问题的流包含二进制序列化协议中的消息。
我已经追踪到以下内容
- 它仅在读取 unicode 字符串时发生(使用 Encoding.BigEndian 编码)
- 仅当有问题的字符串拆分为两个 tcp 数据包时才会发生(使用 wireshark 确认)
我认为正在发生的事情如下(在下面的例子中)
- 调用 BinaryReader.ReadChars() 要求它读取 3 个字符(字符串长度在字符串本身之前编码)
- 第一个循环在内部请求从网络流中读取 6 个字节(剩余 3 个字符 * 2 个字节/字符)
- 网络流只有 3 个字节可用
- 3 个字节读入本地缓冲区
- 缓冲区交给解码器
- 解码器解码 1 个字符,并将另一个字节保存在它自己的内部缓冲区中
- 第二个循环内部请求读取 4 个字节!(2 个剩余字符 * 2 个字节/字符)
- 网络流有全部 4 个字节可用
- 4字节读入本地缓冲区
- 缓冲区交给解码器
- 解码器解码 2 个字符,并在内部保留剩余的第 4 个字节
- 字符串解码完成
由于流损坏,序列化代码尝试解组下一个项目并发出呱呱叫声。
char[] buffer = new char[3]; int charIndex = 0; Decoder decoder = Encoding.BigEndianUnicode.GetDecoder(); // pretend 3 of the 6 bytes arrives in one packet byte[] b1 = new byte[] { 0, 83, 0 }; int charsRead = decoder.GetChars(b1, 0, 3, buffer, charIndex); charIndex += charsRead; // pretend the remaining 3 bytes plus a final byte, for something unrelated, // arrive next byte[] b2 = new byte[] { 71, 0, 114, 3 }; charsRead = decoder.GetChars(b2, 0, 4, buffer, charIndex); charIndex += charsRead;
我认为根是 .NET 代码中的一个错误,它使用 charsRemaining * bytes/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。为了解决这个问题,我只是自己计算所需的字节,读取它们,然后通过相关的 Encoding.GetString() 运行 byte[]。但是,这不适用于 UTF-8 之类的东西。
有兴趣听听人们对此的想法以及我是否做错了什么。也许它会为下一个人节省几个小时/几天的繁琐调试。
编辑:张贴连接连接跟踪项目