2 votes

Socket asynchrone, réception de messages sous forme de chaînes de caractères

Bonsoir, désolé d'avance d'écrire autant mais je ne sais pas où est l'erreur...

Mon application cliente reçoit des données du serveur de manière asynchrone. Je souhaite transférer un grand nombre de données en une seule fois (le contenu d'un tableau, quelques centaines d'octets).

Je veux 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 est le suivant "print_hello" il devrait appeler une fonction qui imprime hello.

Si j'ai bien compris, 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), je dois donc stocker toutes les données dans une mémoire tampon, et lorsqu'une "fin de commande" (par exemple, "end of command") est envoyée, je dois la stocker dans un tampon, '!' ) a été reçu, il doit savoir qu'il doit appeler la fonction.

Jusqu'à présent, cela me semble logique, mais j'ai du mal à 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é comme volatile static string mainBuffer = ""; La première ligne s'imprime correctement et parcourt toutes les données comme prévu. Cependant, lorsque j'imprime la ligne mainBuffer Il n'imprime que le tout premier ensemble de données que j'ai reçu, le reste n'est pas ajouté à la mémoire tampon.

Quelle en est la cause ? Des problèmes de sécurité du fil ? Est-ce que je ne lis pas la dernière valeur de mainBuffer ? Je ne peux pas utiliser de points d'arrêt pour déboguer ce problème.

Exemple de sortie :

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

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

3voto

Jalal Aldeen Saa'd Points 9120

Voici votre problème "avec les lignes de code en désordre !" :

//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 celle que vous avez mentionnée précédemment. Voici ce qui se passe :

Les string en C# est un tableau de char commence par un pointeur sur le premier char dans le tableau et se termine par le caractère spécial "terminal". \0 .
Lorsque vous créez un tableau d'octets de n il remplira tous les index du tableau avec la valeur par défaut de byte qui est 0 . mais 0 est juste égal au caractère terminal \0

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

Ainsi, lorsque vous appelez Read(buffer) la méthode ne sera pas garniture la mémoire tampon à seulement s'adapter Ainsi, si la taille de la mémoire tampon "ici 1024" est supérieure aux données lues, tous les octets restants de la mémoire tampon seront égaux au caractère terminal '. \0 Le tableau de caractères de la chaîne générée sera donc le suivant ABC\0\0\0\0... to the index 1024 . Lorsque vous ajoutez une chaîne DEF à cela, il l'ajoutera au dernier indice de l'article. char tableau "après le dernier \0 ", le char sera alors ABC\0\0\0\0...DEF mais à cause de DEF est ajouté après le(s) caractère(s) terminal(aux) de sorte que l'élément Console.Write ignorera tout ce qui se trouve après le premier \0 !.

Notez également que lors du débogage, si vous pointez votre souris sur le bouton mainBuffer vous verrez que les données qu'elle contient peuvent ressembler à quelque chose comme ABC\0\0\0\0..DEF\0\0\0\0..GHI

Cependant, pour résoudre le problème et ne générer qu'une chaîne de caractères fiable, récupérez les octets lus et ne générez la chaîne de caractères qu'à partir de ces octets. C'est ainsi qu'il faut procéder :

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 utile de mentionner ici que vous devriez envisager d'utiliser StringBuilder au lieu de string .

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X