2017-04-13 18 views
-1

の終了を決定し、私はシリアライズ午前の形式は次のとおりです。にBinaryFormatter - 私はTCP messages.Theクラスを送信するためにBinaryFormatter.Serializeメソッドを使用していたメッセージ

[Serializable] 
public class Message { 
     public int senderId; 
     public int metaData; 
     public foo moreMetaData; 
     public object[] message; 
} 

私は一般的に、ということを知っています、任意のメッセージの終わりを決定するための3つの方法がありますメッセージの

  • プリペンドサイズバイト
  • 追加端が
  • 固定メッセージ長
  • バイト

第3のオプションはひどい考えのようです。 2番目のオプションを使用すると、ストリームにバイトを追加できますが、受信側でBinaryFormatter.deserializeを呼び出すことはできますか?私が最初のオプションを使用している場合(残念ながらリストを逆順に)、オプション2と同じ問題があります(前置きを除いて)。シリアル化する前にシリアル化のサイズを決定するという追加の問題があります。一度ダミー変数に入れてサイズを決定してから、もう一度実際のストリームバッファに入れ直す必要はありません。ここでは一般的に何が行われますか?

答えて

1

BinaryFormatterはすでに「Prepend size byte」を内部的に実装しています。 オブジェクトをBinaryFormatter.Deserializeメソッドに渡すだけで済みますし、読み込む必要のあるバイト数を独自に把握することができます。

注: BinaryFormatterは、アセンブリのバージョンのばらつきに対して非常に敏感です。片方のプログラムのバージョンが一方のバージョンにあり、もう一方のバージョンがもう少し古いバージョンの場合は、2つの端末がお互いに対話できないことがあります。モデルをアセンブリバージョン番号に結び付けないバイナリシリアライザを使用することをお勧めします。代わりにProtoBuf-netは良いライブラリです。 EDIT

:ここあなたは私が `バイト[] BUF =新しいバイトを使用してい

private async Task MessageLoop(NetworkStream networkStream) 
{ 
    //Lets pretend our protocall sends a byte with: 
    // - 1 if the next object will be a Foo, 
    // - 2 if the next object will be a Bar 
    // - 3 if the next object will be a Int32. 

    var formatter = new BinaryFormatter(); 
    byte[] buffer = new byte[1024]; 

    while (true) 
    { 
     var read = await networkStream.ReadAsync(buffer, 0, 1).ConfigureAwait(false); 
     if (read < 0) 
     { 
      await LogStreamDisconnectAsync(); 
     } 

     switch (buffer[0]) 
     { 
      case 1: 
       //If we are on a SynchronizationContext run the deseralize function on a new thread because that call will block. 
       Func<Foo> desearalize =()=> (Foo)formatter.Deserialize(networkStream); 
       Foo foo; 
       if (SynchronizationContext.Current != null) 
       { 
        foo = await Task.Run(desearalize).ConfigureAwait(false); 
       } 
       else 
       { 
        foo = desearalize(); 
       } 

       await ProcessFooAsync(foo).ConfigureAwait(false); 
       break; 
      case 2: 
       var bar = await Task.Run(() => (Bar)formatter.Deserialize(networkStream)).ConfigureAwait(false); 
       await ProcessBarAsync(bar).ConfigureAwait(false); 
       break; 
      case 3: 

       //We have to loop on Read because we may not get 4 bytes back when we do the call, so we keep calling till we fill our buffer. 
       var bytesRead = 0; 
       while (bytesRead < 4) 
       { 
        //We don't want to overwrite the buffer[0] so we can see the value in the debugger if we want, so we do 1 + bytesRead as the offset. 
        bytesRead += await networkStream.ReadAsync(buffer, 1 + bytesRead, 4 - bytesRead).ConfigureAwait(false); 
       } 

       //This assumes both ends have the same value for BitConverter.IsLittleEndian 
       int num = BitConverter.ToInt32(buffer, 1); 

       await DoSomethingWithANumberAsync(num).ConfigureAwait(false); 

       return; 
      default: 
       await LogInvaidRequestTypeAsync(buffer[0]).ConfigureAwait(false); 
       return; 
     } 
    } 

} 
+0

をそれを行うことができる方法の一例である[1024]。 バッファからメッセージを読み取るには、networkStream.ReadAsync(buf、0、buf.Length); ' を待ちます。 BinaryFormatter.deserializeを呼び出すタイミングを決定するにはどうすればよいですか? – WreckFish

+0

@ WreckFishあなたはReadAsyncで次のメッセージタイプが何であるかを伝えるだけで十分です。ストリームをDesearalize関数に渡します。私は答えを例で更新しました。 –

+0

ありがとうございます。それはより明確になっています。しかし、私はまだ例についていくつか質問があります。 'var read = await networkStream.ReadAsync(buffer、0、1).ConfigureAwait(false);'の後に残りのデータがいつ受信されるかは、どのようにして決まりますか?私はこれが 'SynchronizationContext.Current'と' ConfigureAwait(false) 'と関係していると仮定していますが、これらのことが何であるか、あるいは何かを理解していません。また、なぜここに非同期呼び出しがたくさんあるのですか?データが受信されると、deserialize()やprocessFoo()などの呼び出しを同期して行うことはできませんか? – WreckFish

関連する問題