Я столкнулся с проблемой метода BinaryReader.ReadChars (). Когда я оборачиваю BinaryReader вокруг необработанного сокета NetworkStream, иногда я получаю повреждение потока, когда считываемый поток выходит из синхронизации. Данный поток содержит сообщения в двоичном протоколе сериализации.
Я отследил это до следующего
- Это происходит только при чтении строки 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;
Я думаю, что root - это ошибка в коде .NET, которая использует charsRemaining * bytes / char в каждом цикле для вычисления оставшихся необходимых байтов. Из-за дополнительного байта, скрытого в декодере, этот расчет может быть отключен на один, вызывая расход дополнительного байта из входного потока.
Вот код .NET Framework, о котором идет речь
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.
Интересно услышать мысли людей по этому поводу и узнать, делаю ли я что-то не так или нет. И, возможно, это сэкономит следующему человеку несколько часов / дней утомительной отладки.
РЕДАКТИРОВАТЬ: опубликовано для подключения Подключить элемент отслеживания