2017-06-15 1 views
0

Bonjour J'essaie d'envoyer et de recevoir des données entre l'application client/serveur.Liste de désérialisation <T> sur le problème de réseau

Classe

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

sérialiseur et désérialiseur méthodes d'extension:

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

L'exemple des données que je suis en train d'envoyer et de désérialiser est:

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

Envoi avec le code suivant :

 NetworkStream broadcastStream = statusSocket.GetStream(); 

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

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

Réception du code de flux. ignore la boucle n.DataAvailable. J'envoie à des fins de test de très petits paquets bien en dessous du tampon qui est transféré en une partie.

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

Le code de réception déclenchera un événement avec les données comme octet [] défini comme message. Sur mon événement de traitement j'essaie de désérialiser avec:

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

Sur l'étape de deserialize il lancera une exception (exception levée: « System.Runtime.Serialization.SerializationException » dans mscorlib.dll) InnerException est nulle .

Lorsque j'utilise les méthodes d'extension sans les envoyer sur réseau avec quelques exemples de données, la désérialisation fonctionne parfaitement.

J'ai également essayé de jouer avec les tailles de mémoire tampon de réception. Cela ne semble pas aider aussi. L'exemple de taille de données est inférieur à 1024 * 8.

Si la longueur des données d'envoi est de 600 octets par exemple. Le tampon final de réception doit-il également avoir la même taille? Normalement cela devrait être un problème car il se lit en morceaux.

Après 2 jours, j'abandonne. Toute aide serait appréciée. J'ai essayé de rendre la question informative en plaçant les extraits de code de fonctionnement.

+0

Si la taille de votre message est inférieure à la taille de la mémoire tampon (1024 * 8), le reste de votre mémoire tampon est rempli de zéros, ce qui, bien sûr, empêche la désérialisation correcte. Sans oublier que chaque lecture écrase une partie de la mémoire tampon (dans la boucle 'while DataAvailable'). – Evk

+0

L'utilisation de NetworkStream.Length n'est pas prise en charge. Vous devez donc définir une taille de mémoire tampon fixe. Grand ou petit. Pour le tester j'ai vérifié la taille de mes données d'échantillon il était 606 octets. J'ai ajusté le tampon de réception également à 606 octets la même exception se produit. La boucle while l'écrase en effet. C'est un exemple de code. Les données d'envoi sont bien en dessous de la taille de la mémoire tampon, donc elles ne seront pas bouclées :) – Shift

+0

Au lieu de sérialiser via un 'MemoryStream', vous pourriez fournir' '' '' 'NetworkStream'' au '' BinaryFormatter '' directement. Mais si vous voulez traiter les données manuellement, gardez à l'esprit que les opérations 'Read' renvoient le nombre d'octets réellement lus et peuvent recevoir les données en morceaux plus petits que les données envoyées. La réception de 0 octet indique que l'autre côté a fermé son canal d'envoi. –

Répondre

1

Ce code a plusieurs problèmes:

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

Tout d'abord, l'expéditeur peut envoyer un message octets en morceaux. D'abord vous lirez un morceau, puis vous lirez un autre fragment en écrasant le premier dans le tampon (parce que vous utilisez le même tampon pour toutes les lectures, et vous écrivez toujours à l'index 0). De plus, vous n'avez aucune idée quand le message a vraiment été reçu complètement. L'expéditeur peut envoyer un morceau, puis il y a un délai pour une raison quelconque, à ce stade, votre boucle while (n.DataAvailable) existe et vous essayez de désérialiser un message incomplet. En plus - quand vous avez eu la chance de recevoir le message complet - le reste du tampon (en supposant que la taille du message est plus petite que la taille de la mémoire tampon) est rempli de zéros qui empêchent malgré tout la désérialisation réussie.

En outre, ce n'est pas une bonne idée d'utiliser BinaryFormatter pour sérialiser des objets sur le réseau. Au lieu de cela, utilisez quelque chose comme protobuf.

Pour corriger votre code réseau, envoyez d'abord la longueur de votre message sérialisé. Si vous avez différents types de message - envoyez également un type de message. Après l'envoi d'une longueur (et le type si nécessaire) - envoyer un message lui-même:

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

Puis sur le côté réception:

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 

Notez que ce code est non testé, donc attention.

+0

J'ai compris ça. Les données sont très petites pour provoquer un problème de tampon. J'envoie un petit paquet de 606 octets. Je prendrai soin de ce morceau plus tard quand les travaux de désérialisation. – Shift

+0

Mais il y a d'autres problèmes listés ici, comme un buffer rempli de zéros, parce que vous ne connaissez pas la taille de la mémoire tampon. – Evk

+0

Comme je l'ai mentionné ci-dessus avec le tampon de réception mis à 606 (sur la base de ce que j'envoie) sans zéros, il ne sera toujours pas le désérialiser. – Shift

0

Je suggère d'utiliser WCF, .NET Remoting ou quelque chose de comparable. Ils sont conçus exactement dans ce but.

Si vous écrivez votre propre canal de transmission, vous devez normalement intégrer les données sérialisées dans un protocole de communication de données. Généralement, il contient une longueur de message et d'autres informations sur les données transmises. Ceci est requis, par ex. pour permettre au récepteur de connaître la taille des données/messages et le type de données. Du côté du récepteur, normalement, vous n'avez aucune idée du nombre d'octets à recevoir et de ce qu'ils sont.