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?
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. –
Merci Scott – joelc
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 –