7

NetworkStreamから読み込んでいるバイト配列があります。最初の2バイトは後続のパケットの長さを示し、次にパケットはその長さのバイト配列に読み込まれます。NetworkStream / byte配列から読み取る必要があるデータには、いくつかの文字列があります。つまり、改行文字で終了する可変長データと、bytesやlongsなどの固定幅フィールドがあります。だから、このようなもの:

// I would have delimited these for clarity but I didn't want
// to imply that the stream was delimited because it's not.
StringbyteStringStringbytebytebytelonglongbytelonglong

私は出くわすデータパケットのフォーマットを知っています(そしていくつかの意見があります)、そして私がする必要があるのは各文字列値の「行」を読み取ることですが、バイトとロングの固定数のバイトを読み取ります。これまでのところ、私の提案する解決策は、whileループを使用して、改行文字ができるまでバイトを一時バイト配列に読み込むことです。次に、バイトを文字列に変換します。これは私には不器用に思えますが、別の明白な方法はわかりません。私は使用できることを認識していますStreamReader.ReadLine()が、それは別のストリームを含み、私はすでにを持っていNetworkStreamます。しかし、それがより良い解決策である場合、私はそれを試してみます。

私が検討したもう1つのオプションは、バックエンドチームに、これらの文字列値の長さに対して1バイトまたは2バイトを書き込んでもらい、長さを読み取ってから、指定された長さに基づいて文字列を読み取ることです。

ご覧のとおり、これを実行する方法についていくつかのオプションがあります。それを実行するための最良の方法を検討する方法について、ご意見をお聞かせください。これが、パケット全体を文字列として読み取るために今持っているコードです。次のステップは、パケットのさまざまなフィールドを分割し、パケット内のデータに基づいてオブジェクトの作成、UIの更新など、実行する必要のある実際のプログラミング作業を実行することです。

string line = null;  
while (stream.DataAvailable)
{  
    //Get the packet length;  
    UInt16 packetLength = 0;  
    header = new byte[2];  
    stream.Read(header, 0, 2);  
    // Need to reverse the header array for BitConverter class if architecture is little endian.  
    if (BitConverter.IsLittleEndian)
        Array.Reverse(header);  
    packetLength = BitConverter.ToUInt16(header,0);

    buffer = new byte[packetLength];
    stream.Read(buffer, 0, BitConverter.ToUInt16(header, 0));
    line = System.Text.ASCIIEncoding.ASCII.GetString(buffer);
    Console.WriteLine(line);
}
4

2 に答える 2

9

個人的には

  1. 文字列の先頭にInt16を配置すると、文字列の長さがわかり、
  2. IO.BinaryReaderクラスを使用して読み取りを行います。これは、int、strings、charsなどを変数に「読み取り」ます。たとえば、BinReader.ReadInt16()は2バイトを読み取り、それらが表すint16を返し、2バイトを移動します。ストリーム

お役に立てれば。

PS ReadStringメソッドの使用には注意してください。これは、文字列の前にカスタム7ビット整数が付加されていること、つまりBinaryWriterクラスによって書き込まれたことを前提としています。以下は、このCodeGuruの投稿からのものです

BinaryWriterクラスには、文字列を書き込むための2つのメソッドがあります。オーバーロードされたWrite()メソッドとWriteString()メソッドです。前者は、クラスが使用しているエンコーディングに従って、文字列をバイトのストリームとして書き込みます。WriteString()メソッドも指定されたエンコーディングを使用しますが、文字列のバイトストリームの前に文字列の実際の長さを付けます。このようなプレフィックス付きの文字列は、BinaryReader.ReadString()を介して読み戻されます。

長さの値について興味深いのは、このサイズを保持するために可能な限り少ないバイトが使用され、7ビットでエンコードされた整数と呼ばれる型として格納されることです。長さが7ビットに収まる場合は、1バイトが使用され、これより大きい場合は、最初のバイトの上位ビットが設定され、値を7ビットシフトして2番目のバイトが作成されます。これは、値を保持するのに十分なバイトができるまで、連続するバイトで繰り返されます。このメカニズムは、長さがシリアル化された文字列が占めるサイズの重要な部分にならないようにするために使用されます。BinaryWriterとBinaryReaderには、7ビットでエンコードされた整数を読み書きするメソッドがありますが、これらは保護されているため、これらのクラスから派生した場合にのみ使用できます。

于 2009-01-29T16:54:44.450 に答える
5

長さがプレフィックスされた文字列を使用します。これにより、作業が大幅に簡素化され、改行を含む文字列を表現できるようになります。ただし、コードに関するいくつかのコメント:

  • Stream.DataAvailable を使用しないでください。現在利用可能なデータがないからといって、ストリームの最後まで読んだことにはなりません。
  • ASCII 以外のテキストが必要ないことが絶対に確実でない限り、ASCIIEncoding を使用しないでください。
  • Stream.Read が要求したすべてのデータを読み取ると想定しないでください。常に戻り値を確認してください。
  • BinaryReader を使用すると、これの多くがはるかに簡単になります (長さのプレフィックス付き文字列と、要求された内容が読み取られるまでループする Read を含む)。
  • 同じデータに対して BitConverter.ToUInt16 を 2 回呼び出しています。なんで?
于 2009-01-29T17:11:58.440 に答える