2017-06-15 6 views
0

こんにちは、私はクライアント/サーバーアプリケーション間でいくつかのデータを送受信しようとしています。デシリアライズリスト<T>ネットワークストリームでの問題

クラス

[Serializable] 
public class ScanSessie 
{ 
    public string UserId { get; set; } 
    public int TotalScanned { get; set; } 
    public string Status { get; set; } 
    public string DeviceId { get; set; } 
} 

シリアライザとデシリアライザ拡張メソッド:私はそれを送信し、デシリアライズしようとしています

public static class SerializerDeserializerExtensions 
{ 
    public static byte[] Serializer(this object _object) 
    { 
     byte[] bytes; 
     using (var _MemoryStream = new MemoryStream()) 
     { 
      IFormatter _BinaryFormatter = new BinaryFormatter(); 
      _BinaryFormatter.Serialize(_MemoryStream, _object); 
      bytes = _MemoryStream.ToArray(); 
     } 
     return bytes; 
    } 

    public static T Deserializer<T>(this byte[] _byteArray) 
    { 
     T ReturnValue; 

     using (var _MemoryStream = new MemoryStream(_byteArray)) 
     { 
      IFormatter _BinaryFormatter = new BinaryFormatter(); 
      ReturnValue = (T)_BinaryFormatter.Deserialize(_MemoryStream); 
     } 
     return ReturnValue; 
    } 
} 

例のデータは次のとおりです。

次のコードで送信
List<ScanSessie> scannerCl = new List<ScanSessie>(); 
    scannerCl.Add(new ScanSessie { DeviceId = "0x00456321", UserId = "123456", Status = "scannen ...", TotalScanned = 0 }); 
    scannerCl.Add(new ScanSessie { DeviceId = "0x00456321", UserId = "123456", Status = "scannen ...", TotalScanned = 0 }); 
    scannerCl.Add(new ScanSessie { DeviceId = "0x00456321", UserId = "123456", Status = "scannen ...", TotalScanned = 0 }); 
    scannerCl.Add(new ScanSessie { DeviceId = "0x00456321", UserId = "123456", Status = "scannen ...", TotalScanned = 0 }); 
    scannerCl.Add(new ScanSessie { DeviceId = "0x00456321", UserId = "123456", Status = "scannen ...", TotalScanned = 0 }); 
    scannerCl.Add(new ScanSessie { DeviceId = "0x00456321", UserId = "123456", Status = "scannen ...", TotalScanned = 0 }); 

 NetworkStream broadcastStream = statusSocket.GetStream(); 

     Byte[] broadcastBytes = SerializerDeserializerExtensions.Serializer(scannerCl); 

     broadcastStream.Write(broadcastBytes, 0, broadcastBytes.Length); 
     broadcastStream.Flush(); 

ストリームコードを受信して​​います。 n.DataAvailableループを無視します。私はテスト目的のために、ある部分で転送されるバッファの下に非常に小さなパケットの方法で送信しています。

using (TcpClient client = new TcpClient()) 
       { 
        await client.ConnectAsync("10.184.32.39", 8888); 

        ConnectionEstabilished?.Invoke(); 

        byte[] message = new byte[1024 * 8]; 
        int bytesRead; 

        using (NetworkStream n = client.GetStream()) 
        { 
         while (true) 
         { 
          bytesRead = 0; 

          try 
          { 
           if(n.CanRead) 
           { 
            do 
            { 
             bytesRead = await n.ReadAsync(message, 0, message.Length); 
            } 
            while (n.DataAvailable); 
           } 
           //bytesRead = await n.ReadAsync(message, 0, 4096); 
           //bytesRead = await n.ReadAsync(message, 0, message.Length); 
          } 
          catch (Exception ex) 
          { 
           // some error hapens here catch it   
           Debug.WriteLine("DashboardClientConnect " + ex.Message + "\n" + ex.InnerException); 
           break; 
          } 
         } 

受信コードは、メッセージとして定義されたbyte []のデータを持つイベントを発生させます。それは例外がスローされますデシリアライズステップに

private void Sc_NewDataReceived(byte[] scanner) 
{ 
    try 
    { 
     var result = scanner.Deserializer<List<ScanSessie>>(); 
    } 
    catch(Exception ex) 
    { 
     Debug.WriteLine(ex.InnerException); 
    } 
} 

(例外がスローさ:がmscorlib.dllで「System.Runtime.Serialization.SerializationException」)私はそれをデシリアライズしようとする私の処理イベントで のInnerExceptionがnull 。

ネットワークを介していくつかのサンプルデータを送信せずに拡張メソッドを使用すると、デシリアライズは完全に機能します。

また、私は受信バッファサイズで遊んでみました。それはまた助けているようではありません。データサイズの例は1024 * 8以下です。

たとえば、送信データ長が600バイトの場合。受信側のバッファも同じサイズでなければなりませんか?通常、チャンクを読み込むので問題になるはずです。

2日後、私はあきらめます。どんな助けもありがとう。私は機能的なコードスニペットを配置して質問を有益にしようとしました。

+0

メッセージサイズがバッファサイズ(1024 * 8)より小さい場合は、バッファの残りの部分がゼロで埋められます。これにより、正しく逆シリアル化されません。言うまでもなく、各読み取りはバッファの一部を上書きします( 'while DataAvailable'ループ内)。 – Evk

+0

NetworkStream.Lengthの使用はサポートされていないため、固定バッファサイズを定義する必要があります。大なり小なり。 これをテストするために、サンプルデータのサイズを確認しました。これは606バイトでした。受信バッファも同じ例外が発生する606バイトに調整しました。 whileループが実際に上書きしています。サンプルコードです。送信データはバッファサイズよりも小さいので、ループしません:) – Shift

+0

'MemoryStream'を介してシリアル化するのではなく、' NetworkStream'を 'BinaryFormatter'に直接提供することができます。しかし、手動でデータを処理したい場合は、 'Read'演算が実際に読み取られたバイト数を返し、送られたデータよりも小さなチャンクでデータを受け取ることに注意してください。 0バイトを受信すると、相手側が送信チャネルを閉じたことを示します。 –

答えて

1

このコードでは、いくつかの問題があります。メッセージを送信することがあります

do 
{ 
    bytesRead = await n.ReadAsync(message, 0, message.Length); 
} 
while (n.DataAvailable); 

まず、送信者はチャンクのバイト。最初に1つのチャンクを読み込み、最初にバッファに上書きする別のチャンクを読み込みます(同じバッファをすべての読み込みに使用し、常にインデックス0に書き込むため)。

また、メッセージが本当に完全に受信されたかどうかはわかりません。 Senderが1つのチャンクを送信した場合、何らかの理由で遅延が発生する可能性があります。この時点では、while (n.DataAvailable)ループが存在し、不完全なメッセージを逆シリアル化しようとしています。その上に

- フルメッセージを受信することがラッキーだった - バッファの残りとにかく成功したデシリアライゼーションを防止ゼロで満たされる(メッセージの長さを想定し、バッファサイズが小さいこと)。

また、BinaryFormatterを使用してネットワーク経由でオブジェクトをシリアル化することはお勧めできません。代わりにprotobufのようなものを使用してください。

ネットワークコードを修正するには、まずシリアル化されたメッセージの長さを送信します。メッセージタイプが異なる場合は、メッセージタイプも送信します。このコードが未検証であること

byte[] lengthBuffer = new byte[sizeof(int)]; 
var bytesRead = await n.ReadAsync(lengthBuffer, 0, lengthBuffer.Length); 
// check if bytesRead equals buffer size 
if (!BitConverter.IsLittleEndian) 
    Array.Reverse(lengthBuffer); 
var length = BitConverter.ToInt32(lengthBuffer, 0); 
// check if length is not too big, otherwise you will crash process with out of memory 
var messageBuffer = new byte[length]; 
bytesRead = await n.ReadAsync(messageBuffer, 0, messageBuffer.Length); 
// check if bytesRead equals buffer size 
// now you can deserialize 

注ので、世話をする:受信側で次に

NetworkStream broadcastStream = statusSocket.GetStream(); 
Byte[] broadcastBytes = SerializerDeserializerExtensions.Serializer(scannerCl); 
var lengthBytes = BitConverter.GetBytes(broadcastBytes.Length); 
if (!BitConverter.IsLittleEndian) 
    Array.Reverse(lengthBytes); 
broadcastStream.Write(lengthBytes, 0, lengthBytes.Length); 
broadcastStream.Write(broadcastBytes, 0, broadcastBytes.Length); 
broadcastStream.Flush(); 

: - (必要に応じてタイプ)長さを送信した後、メッセージ自体を送信します。

+0

私はそれを得た。データは非常に小さく、バッファの問題を引き起こします。私は606バイトの小さなパケットを送信しています。私は後でその作品をデシリアライズするときに世話をします。 – Shift

+0

しかし、そこには、バッファサイズがわからないため、ゼロで埋められたバッファなどの他の問題があります。 – Evk

+0

上記のように、受信バッファが606に設定されている(私が送信しているものに基づいて)0でなくても、それをまだデシリアライズしません。 – Shift

0

WCF、.NET Remotingなどを使用することをお勧めします。それらはその目的のために設計されています。

独自の伝送チャネルを作成する場合は、通常、シリアル化されたデータをデータ通信プロトコルに組み込む必要があります。通常、これにはメッセージの長さや送信されるデータに関するその他の情報が含まれます。これは必須です(例:受信者にデータ/メッセージサイズとデータタイプを知らせる。受信側では、通常、何バイトを受信しなければならないか、何バイトであるかは分かりません。