2016-04-19 1 views
2

J'ai une application qui utilise un socket TCP pour échanger des tableaux d'octets qui dans la plupart des cas contiennent des données de chaîne JSON. Ce que je vis, c'est que, pour des messages plus importants et des conditions de réseau moins qu'idéales, l'utilisation de NetworkStream.DataAvailable ne semble PAS être un moyen fiable de détecter une fin de message. Il semble que dans certains cas, DataAvailable est défini sur false même si seule une partie du message a été transmise par un homologue (qui utilise TcpClient.GetStream().Write(data, 0, data.Length). Il en résulte que les données incomplètes sont renvoyées à l'application, ce qui, dans le cas d'un message JSON, signifie que la désérialisation échoue.C# NetworkStream.DataAvailable ne semble pas fiable

J'ai essayé deux implémentations qui présentent la même question:

Application 1:

byte[] Data; 
byte[] buffer = new byte[2048]; 
using (MemoryStream ms = new MemoryStream()) 
{ 
    int read; 

    while ((read = ClientStream.Read(buffer, 0, buffer.Length)) > 0) 
    { 
     ms.Write(buffer, 0, read); 
     BytesRead += read; 

     if (!ClientStream.DataAvailable) break; 
    } 

    Data = ms.ToArray(); 
} 

Mise en œuvre 2:

byte[] Data; 
using (MemoryStream ms = new MemoryStream()) 
{ 
    byte[] buffer = new byte[2048]; 

    do 
    { 
     int read = ClientStream.Read(buffer, 0, buffer.Length); 
     if (read > 0) 
     { 
      ms.Write(buffer, 0, read); 
      BytesRead += read; 
     } 
    } 
    while (ClientStream.DataAvailable); 

    Data = ms.ToArray(); 
} 

Il semble une solution qui fonctionne vraiment bien, mais est complètement sous-optimale est d'ajouter un Thread.Sleep au cas où NetworkStream.DataAvailable est faux (alors que dans la boucle) pour permettre la livraison des données. Cependant, cette limite sévèrement IOPS ensemble que je voudrais éviter, à savoir

mise en œuvre 3 (œuvres, mais suboptimale)

byte[] Data; 
using (MemoryStream ms = new MemoryStream()) 
{ 
    byte[] buffer = new byte[2048]; 

    do 
    { 
     int read = ClientStream.Read(buffer, 0, buffer.Length); 
     if (read > 0) 
     { 
      ms.Write(buffer, 0, read); 
      BytesRead += read; 
     } 

     if (!ClientStream.DataAvailable) System.Threading.Thread.Sleep(250); 
    } 
    while (ClientStream.DataAvailable); 

    Data = ms.ToArray(); 
} 

Je voudrais vraiment trouver un moyen de rester dans la boucle jusqu'à ce que tous les données sont livrées. Comme je l'ai mentionné, je fais une simple opération d'écriture sur le client de zéro à la longueur des données, donc je ne pense pas qu'il y ait un problème là.

Avez-vous déjà eu une expérience comme celle-ci et une recommandation?

Répondre

1

Il semble que .DataAvailable soit fiable et que, puisque les données arrivent sur le réseau potentiellement à un débit plus lent que les données lues dans le flux, .DataAvailable peut basculer entre le début et la fin de ce que pense mon application est un message.

Je réponds et fermer ce que je crois que les seules solutions à ce sont:

1) ajouter une valeur de délai d'attente reçoivent trop arquées, et effectuer une Thread.sleep dans la boucle de lecture, et expirant le 2) implémenter un mécanisme d'indication de la taille de la charge utile de données - soit explicitement, soit en créant un système d'en-têtes de métadonnées - pour indiquer la quantité de données à lire et en sortir après que beaucoup de données ont été saisies. été lu ou l'opération a expiré

Ces deux sont les meilleurs que je pourrais Ils semblent être validés par d'autres protocoles TCP comme HTTP et généralement tous les autres RPC.

Espérons que cela économise du temps.

+4

Le nom formel de l'option numéro 2 est appelé "[Image Framing] (http://blog.stephencleary.com/2009/04/message-framing.html)" et constitue la manière standard de le gérer. –

+0

Merci Scott – joelc

+0

Quelques jours en retard, mais j'ai deux classes construites pour le cadrage de message sous forme de préfixes de longueur et en-tête. Voir (il y a une source C#): http://stackoverflow.com/questions/35233852/tcp-client-to-server-communication/35240061#35240061 –