2014-05-12 1 views
1

J'utilise un réseau pour transmettre des chaînes courtes sur le réseau.
Maintenant, du côté de la réception j'ai rencontré un problème:
Normalement, je ferais la lecture comme celui-ciNetworkStream.Length substitut

  1. voir si les données sont disponibles à tous les
  2. nombre get de données disponibles
  3. lecture que de nombreux octets dans un tampon
  4. convertir le contenu du tampon en chaîne.

Dans le code qui suppose travaillent toutes les méthodes proposées comme probablement pour but, qui ressemblerait à quelque chose comme ceci:

NetworkStream stream = someTcpClient.GetStream(); 
while(!stream.DataAvailable) 
    ; 

byte[] bufferByte; 
stream.Read(bufferByte, 0, stream.Lenght); 
AsciiEncoding enc = new AsciiEncoding(); 
string result = enc.GetString(bufferByte); 

Cependant, MSDN dit que NetworkStream.Length est pas vraiment mis en œuvre et sera toujours jeter une exception lorsqu'il est appelé .
Étant donné que les données entrantes sont de longueur variable, je ne peux pas coder en dur le nombre d'octets à attendre (ce qui serait également le cas de l'antipattern de nombre magique).

Question:
Si je ne peux pas obtenir un décompte précis du nombre d'octets disponibles pour la lecture, alors comment puis-je lire à partir du flux correctement, sans risquer de toutes sortes d'exceptions dans les NetworkStream.Read?

EDIT:
Bien que la réponse fournie conduit à un meilleur code dans l'ensemble je veux encore partager une autre option que je suis tombé sur:
TCPClient.Available donne les octets disponibles à lire. Je savais qu'il devait y avoir un moyen de compter les octets dans sa propre boîte de réception.

+1

[Cela devrait aider] (http://www.codeproject.com/Articles/37496/TCP-IP-Protocol-Design-Message-Framing) –

Répondre

4

Il n'y a aucune garantie que les appels à Read d'un côté de la connexion sera correspondre 1-1 avec des appels à Write de l'autre côté. Si vous avez affaire à des messages de longueur variable, il vous faut pour fournir ces informations au destinataire.

Une façon courante de procéder consiste à déterminer d'abord la longueur du message que vous allez envoyer, puis à envoyer cette information de longueur en premier. Du côté réception, vous obtenez ensuite la longueur en premier, puis vous savez quelle taille un buffer doit allouer. Vous appelez alors Read dans une boucle jusqu'à ce que vous ayez lu le bon nombre d'octets. Notez que, dans votre code d'origine, vous êtes actuellement en ignorant la valeur de retour de Read, qui vous indique combien d'octets ont réellement été lus. En un seul appel et retour, cela peut être aussi bas que 1, même si vous demandez plus de 1 octet.

Une autre manière courante est de décider des "formats" de message - où par ex. le numéro de message 1 est toujours de 32 octets et a la structure X, et le numéro de message 2 a une longueur de 51 octets et a la structure Y.Avec cette approche, plutôt que d'envoyer le message longueur avant d'envoyer le message, vous envoyez plutôt les informations de format - d'abord vous envoyez "voici un message de type 1" et ensuite vous envoyez le message. Un autre moyen commun, le cas échéant, est d'utiliser une certaine forme de sentinelles - si vos messages ne contiennent jamais, disons, un octet de valeur 0xff alors vous scannez les octets reçus jusqu'à ce que vous ayez reçu un octet 0xff, et alors tout ce qui précède cet octet était le message que vous vouliez recevoir. Mais, peu importe ce que vous voulez faire, que ce soit l'approche ci-dessus, ou autre chose, c'est pour que vos côtés d'envoi et de réception fonctionnent ensemble pour permettre au récepteur de découvrir chaque message.


J'oubliais de dire, mais une autre façon de changer tout est autour - si vous voulez échanger des messages , et ne veulent pas faire tout ce qui précède bidouiller, puis passer à quelque chose qui fonctionne à un niveau supérieur - par exemple WCF, ou HTTP, ou quelque chose d'autre, où ces systèmes prennent déjà en charge le cadrage des messages et vous pouvez alors vous concentrer sur ce qu'il faut faire de vos messages.

+0

J'utilise le codage ASCII, donc je pourrais par exemple utiliser 0x04 Fin de transmission. Quelle coïncidence qu'un tel personnage existe. Merci beaucoup. Je pensais que depuis la lecture est mon côté du travail, que je pourrais juste jeter un oeil dans * mon * flux et lire autant. – Mark

+2

@Mark, oui c'est presque comme ASCII était basé sur [un protocole qui courait à l'origine sur les réseaux] (http://en.wikipedia.org/wiki/Teleprinter). –

+0

@ScottChamberlain magie ... – Mark

1

Vous pouvez utiliser StreamReader lire flux à la fin

var streamReader = new StreamReader(someTcpClient.GetStream(), Encoding.ASCII); 

string result = streamReader.ReadToEnd(); 
+1

Ceci bloquera jusqu'à ce que le socket soit fermé. –

+0

Comment pouvez-vous savoir que le flux est terminé? –

+0

@VladimirSachek Je veux vraiment utiliser ceci pour la communication, donc je veux garder le socket ouvert pendant un certain temps. – Mark