2017-06-15 3 views
0

Hallo Ich versuche, einige Daten zwischen Client/Server-Anwendung zu senden und zu empfangen.Deserialisierung Liste <T> über Netzwerk-Problem

Klasse

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

Serializer und Deserializer Erweiterungsmethoden:

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; 
    } 
} 

Das Beispiel Daten, die ich es versuche, zu senden und deserialisieren ist:

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 }); 

mit dem folgenden Code Senden :

 NetworkStream broadcastStream = statusSocket.GetStream(); 

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

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

Empfangen von Stream-Code. Ignoriere die n.DataAvailable-Schleife. Ich sende zu Testzwecken sehr kleine Pakete weit unterhalb des Puffers, der in einem Teil übertragen wird.

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; 
          } 
         } 

Der Empfangscode löst ein Ereignis mit den Daten als Byte [] aus, das als Nachricht definiert ist. Auf meiner Verarbeitung Ereignis versuche ich es deserialisieren mit:

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

Auf der deserialize Schritt wird eine Ausnahme (Exception geworfen: 'System.Runtime.Serialization.SerializationException' in mscorlib.dll) werfen innerexception ist null .

Wenn ich die Erweiterungsmethoden verwende, ohne sie über ein Netzwerk mit einigen Beispieldaten zu senden, funktioniert die Deserialisierung einwandfrei.

Ich habe auch versucht, mit den Empfangspuffergrößen herumzuspielen. Das scheint auch nicht zu helfen. Die Beispieldatengröße liegt unter 1024 * 8.

Wenn die Sendedatenlänge beispielsweise 600 Bytes beträgt. Muss der empfangende Endpuffer auch die gleiche Größe haben? Normalerweise sollte das ein Problem sein, da es Chunks einliest.

Nach 2 Tagen gebe ich auf. Jede Hilfe wäre willkommen. Ich habe versucht, die Frage informativ zu machen, indem ich die funktionierenden Code-Schnipsel platzierte.

+0

Wenn Ihre Nachrichtengröße kleiner als die Puffergröße (1024 * 8) ist - der Rest Ihres Puffers ist mit Nullen gefüllt, was natürlich eine korrekte Deserialisierung verhindert. Ganz zu schweigen davon, dass jedes Lesen einen Teil des Puffers überschreibt (in der 'wait DataAvailable' Schleife). – Evk

+0

Der Versuch, NetworkStream.Length zu verwenden, wird nicht unterstützt. Daher müssen Sie eine feste Puffergröße definieren. Groß oder klein. Um es zu testen, überprüfte ich die Größe meiner Beispieldaten war es 606 Bytes. Ich habe den Empfangspuffer auch auf 606 Bytes eingestellt, die gleiche Ausnahme tritt auf. Die While-Schleife überschreibt es tatsächlich. Es ist ein Beispielcode. Die Sendedaten liegen weit unter der Puffergröße, so dass sie nicht in die Schleife gehen :) – Shift

+0

Anstatt über einen 'MemoryStream' zu serialisieren, könnten Sie den' NetworkStream' direkt dem 'BinaryFormatter' zur Verfügung stellen. Wenn Sie die Daten jedoch manuell verarbeiten möchten, beachten Sie, dass die 'Read'-Operationen die Anzahl der tatsächlich gelesenen Bytes zurückgeben und die Daten möglicherweise in Blöcken empfangen werden, die kleiner als die gesendeten Daten sind. Der Empfang von 0 Bytes zeigt an, dass die andere Seite ihren Sendekanal geschlossen hat. –

Antwort

1

Dieser Code hat mehrere Probleme:

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

Zuerst Absender könnte Nachrichtenbytes in Blöcken senden. Zuerst lesen Sie einen Chunk, dann lesen Sie einen anderen Chunk, der zuerst im Puffer überschrieben wird (weil Sie für alle Lesevorgänge denselben Puffer verwenden und Sie immer in den Index 0 schreiben).

Auch Sie haben keine Ahnung, wann die Nachricht wirklich vollständig erhalten wurde. Absender sendet möglicherweise einen Chunk, dann gibt es eine Verzögerung aus welchem ​​Grund auch immer, zu diesem Zeitpunkt ist Ihre while (n.DataAvailable) Schleife vorhanden und Sie versuchen, unvollständige Nachricht zu deserialisieren.

Oben drauf - wenn Sie Glück hatten, eine vollständige Nachricht zu erhalten - wird der Rest des Puffers (vorausgesetzt die Nachrichtenlänge ist kleiner als die Puffergröße) mit Nullen gefüllt, was eine erfolgreiche Deserialisierung verhindert.

Auch - es ist keine gute Idee, BinaryFormatter zu verwenden, um Objekte über das Netzwerk zu serialisieren. Verwenden Sie stattdessen etwas wie Protobuf.

Um Ihren Netzwerkcode zu beheben, senden Sie zunächst die Länge Ihrer serialisierten Nachricht. Wenn Sie unterschiedliche Nachrichtentypen haben, senden Sie auch den Nachrichtentyp. Nach dem Senden Länge (und geben Sie, falls erforderlich) - sende Nachricht selbst:

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(); 

Dann auf der Empfangsseite:

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 

Beachten Sie, dass dieser Code nicht getestet ist, so kümmern.

+0

Ich habe das. Die Daten sind sehr klein, um Pufferprobleme zu verursachen. Ich sende ein kleines Paket von 606 Bytes. Ich werde später bei der Deserialisierung auf dieses Stück aufpassen. – Shift

+0

Aber dort sind andere Probleme aufgeführt, z. B. Puffer mit Nullen, weil Sie die Puffergröße nicht kennen. – Evk

+0

Wie ich oben erwähnt habe mit Empfangspuffer auf 606 (basierend auf was ich sende) ohne Nullen wird es immer noch nicht deserialisieren. – Shift

0

Ich empfehle, WCF, .NET Remoting oder etwas Vergleichbares zu verwenden. Sie sind genau für diesen Zweck konzipiert.

Wenn Sie Ihren eigenen Übertragungskanal schreiben, müssen Sie normalerweise die serialisierten Daten in ein Datenkommunikationsprotokoll einbetten. Typischerweise enthält dies eine Nachrichtenlänge und andere Informationen über die übertragenen Daten. Dies ist z.B. dem Empfänger die Daten/Nachrichtengröße und den Datentyp mitteilen. Auf der Empfängerseite haben Sie normalerweise keine Ahnung davon, wie viele Bytes empfangen werden müssen und was sie sind.

Verwandte Themen