2011-07-21 4 views
2

Bonsoir, désolé à l'avance pour écrire tant, mais je ne sais pas où l'erreur est ...socket asynchrone, recevoir des messages chaîne

Mon application cliente reçoit du serveur de manière asynchrone. Je veux transférer un tas de choses à la fois (le contenu d'un tableau, quelques centaines d'octets). Je souhaite que le serveur puisse envoyer des "commandes" et qu'une fonction du côté client agisse en fonction de ces commandes, par exemple si le message du serveur lit "print_hello", il doit appeler une fonction qui imprime bonjour . Maintenant, je crois comprendre que lorsque je reçois des données de manière asynchrone, je ne peux pas savoir quelle quantité de données a été envoyée (ou si plus de données que prévu ont été envoyées), donc j'ai besoin de stocker toutes les données un tampon, et quand un signe "fin de commande" (par exemple, '!') a été reçu, il devrait savoir appeler la fonction.

Jusqu'à présent, cela a du sens pour moi, mais je ne parviens pas à le mettre en œuvre. Dans ma fonction de rappel DataReceived, j'ai ce code:

Console.WriteLine("Raw data: {0}", data)); 
mainBuffer += data; 
Console.WriteLine(mainBuffer); 

mainBuffer est déclarée comme volatile static string mainBuffer = ""; La première ligne se imprime correctement, et passe par toutes les données comme prévu. Cependant, lorsque j'imprime le mainBuffer, il n'imprime que le tout premier jeu de données que j'ai reçu, le reste n'est pas ajouté au tampon.

Ce qui pourrait causer cela? Thread problèmes de sécurité? Est-ce que je ne lis pas la dernière valeur de mainBuffer? Je ne peux pas utiliser les points d'arrêt pour déboguer ceci.

Exemple de sortie:

Raw data: ABC 
ABC 
Raw data: DEF 
ABC 
RAW data: GHI 
ABC 

Petite mise à jour, j'essayé d'utiliser un aussi bien volatile static int, et il incrémente et imprime correctement après chaque DataReceived(). La chaîne n'est toujours pas mise à jour cependant.

+0

Où avez-vous déclaré mainBuffer? Êtes-vous sûr de ne pas le réinitialiser à "" chaque fois que vous recevez? – Eiver

Répondre

3

Voici votre problème « avec les lignes de code de déconner! »:

//of course the problem has noting to do with the string being volatile... 
private volatile string mainBuffer = string.Empty; 

byte[] buffer = new byte[1024]; 

while (networkStream.Read(buffer, 0, buffer.Length) > 0) 
{ 
    string data = System.Text.Encoding.UTF8.GetString(buffer); 
    Console.WriteLine("Raw data: {0}", data)); 
    mainBuffer += data; 
    Console.WriteLine(mainBuffer); 
} 

Naturellement la sortie de ce code sera comme vous l'avez mentionné précédemment. Voici ce qui se passe:

La classe string en C# est un tableau de char début par pointeur vers la première char dans le tableau et se termine par le « terminal » spécial char \0.
Lorsque vous créez un tableau d'octets d'index n, il remplira tous les index du tableau avec la valeur par défaut de byte qui est 0. mais 0 est égal à tout le terminal ombles \0

byte b = (byte)`\0`;\\the value of b will be 0 

Ainsi, lorsque vous appelez Read(buffer), juste adapter les données lues de la méthode ne sera pas couper le tampon. donc si la taille de la mémoire tampon "ici 1024" est plus grande que les données lues, tous les octets restants du tampon seront égaux au terminal '\ 0', donc le tableau de caractères de la chaîne générée sera ABC\0\0\0\0... to the index 1024.Lorsque vous ajoutez une chaîne DEF à ce qu'il ajoutera au dernier indice du tableau char « après la dernière \0 », le tableau char puis sera ABC\0\0\0\0...DEF, mais à cause de DEF est ajouté après le terminal char (s) de sorte que le Console.Write ignorera tout après le premier \0!

Notez également pendant que vous le débogage, si vous pointez votre souris sur la variable mainBuffer, vous verrez les données réelles qu'elle contient peut-être quelque chose comme ABC\0\0\0\0..DEF\0\0\0\0..GHI

Cependant, pour résoudre le problème et générer uniquement une chaîne fiable, Obtenir les octets réels lus et générer la chaîne seulement à partir de lui. Alors:

int dataRead = 0; 

while ((dataRead = networkStream.Read(buffer, 0, buffer.Length)) > 0) 
{ 
    List<byte> actualBuffer = (new List<byte>(buffer)).GetRange(0, dataRead); 

    string data = System.Text.Encoding.UTF8.GetString(actualBuffer.ToArray()); 
    Console.WriteLine("Raw data: {0}", data)); 
    mainBuffer += data; 
    Console.WriteLine(mainBuffer); 
} 

Il est courroucé de mentionner ici que vous devriez envisager d'utiliser StringBuilder au lieu de string.

Questions connexes