BinaryReader.ReadChars() メソッドの問題であると思われる問題に遭遇しました。生のソケット NetworkStream に BinaryReader をラップすると、ストリームが破損し、読み取られているストリームが同期しなくなることがあります。問題のストリームには、バイナリ シリアル化プロトコルのメッセージが含まれています。
私はこれを次のように追跡しました
- Unicode 文字列 (Encoding.BigEndian を使用してエンコードされた) を読み取るときにのみ発生します。
- 問題の文字列が 2 つの tcp パケットに分割されている場合にのみ発生します (wireshark を使用して確認)。
何が起こっているかは次のとおりだと思います(以下の例のコンテキストで)
- BinaryReader.ReadChars() が呼び出され、3 文字を読み取るように要求されます (文字列の長さは、文字列自体の前にエンコードされます)。
- 最初のループは、ネットワーク ストリームから 6 バイト (残りの 3 文字 * 2 バイト/文字) の読み取りを内部的に要求します。
- ネットワーク ストリームには 3 バイトしかありません
- ローカル バッファに読み込まれた 3 バイト
- Decoderに渡されるバッファ
- デコーダーは 1 文字をデコードし、他のバイトを独自の内部バッファーに保持します。
- 2 番目のループは、4 バイトの読み取りを内部的に要求します。(残りの 2 文字 * 2 バイト/文字)
- ネットワーク ストリームには 4 バイトすべてが使用可能です
- ローカル バッファに読み込まれた 4 バイト
- Decoderに渡されるバッファ
- デコーダーは 2 char をデコードし、残りの 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;
ルートは、各ループで charsRemaining * bytes/char を使用して必要な残りのバイトを計算する .NET コードのバグだと思います。Decoder に余分なバイトが隠されているため、この計算は 1 だけオフになる可能性があり、入力ストリームから余分なバイトが消費されます。
問題の .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 などでは機能しません。
これについて人々の考えを聞いて、私が何か間違ったことをしているのかどうかに興味を持ってください. そしておそらく、次の人は数時間/数日の退屈なデバッグを節約できます.
編集:接続追跡アイテムを接続するために投稿されました