8
votes

Problème très étrange envoi de données via des sockets en C #

J'approse pour ce poteau long. Je l'ai rendu aussi petit que possible tout en transmettant le problème.

ok cela me rend fou. J'ai un client et un programme de serveur, à la fois en C #. Le serveur envoie des données au client via Socket.Send (). Le client reçoit les données via Socket.beginreceive et Socket.Receive. Mon pseudo-protocole est le suivant: Le serveur envoie une valeur à deux byges (courte) indiquant la longueur des données réelles suivies immédiatement par les données réelles. Le client lit les deux premiers byes asynchrones, convertit les octets en une courte et lit immédiatement que de nombreux octets de la prise de manière synchrone.

Maintenant, cela fonctionne bien pour un cycle toutes les quelques secondes environ, mais lorsque j'augmente la vitesse, les choses deviennent bizarres. Il semble que le client lira au hasard les données réelles lorsqu'elles tentent de lire de la longueur de deux octets. Il essaie ensuite de convertir ces deux octets arbitraires en un bref entraînement à une valeur complètement incorrecte, ce qui entraîne un accident. Le code suivant vient de mon programme mais coupé pour afficher uniquement les lignes importantes.

Méthode côté serveur pour l'envoi de données: xxx

méthode côté client Pour recevoir des données: xxx

code côté serveur pour recréer le problème de manière fiable: xxx

regarder le code ci-dessus, On peut déterminer que le serveur envoie à jamais le numéro 1024, la longueur des données totales, y compris le préfixe, comme un court (0x400) suivi de "FRFX" dans ASCII binaire suivi d'un tas de 7 (0x07). Le client lira à jamais les deux premiers octets (0x400), interpréter cela comme étant 1024, stocker cette valeur sous N, puis lire 1024 byes du flux.

C'est en effet ce qu'il fait pour les 40 premières itérations, mais spontanément, le client lira les deux premiers byes et les interprétera comme étant 1799, pas 1024! 1799 en hexagone est 0x0707 qui est deux 7 consécutifs !!! C'est la donnée, pas la longueur! Qu'est-il arrivé à ces deux octets? Cela se produit avec la valeur que j'ai mise dans le tableau d'octets, je viens de choisir 7 car il est facile de voir la corrélation avec 1799.

Si vous lisez toujours à ce stade, j'applaudis votre dévouement.

Certaines observations importantes:

  • La diminution de la longueur de B augmentera le nombre d'itérations avant que le problème ne se produise, mais n'empêche pas le problème de se produire
  • Ajout d'un délai important entre chaque itération de la boucle peut empêcher le problème de se produire
  • Ce n'est pas lors de l'utilisation du client et du serveur sur le même hôte et la même connexion via une adresse de bouclage.

    Comme mentionné, cela me rend fou! Je suis toujours capable de résoudre mes problèmes de programmation, mais celui-ci m'a complètement ticulé. Donc, je suis ici plaidant pour tout conseiller ou connaissances sur ce sujet.

    merci.


0 commentaires

5 Réponses :


0
votes

Je suppose que smallbuffer est déclaré en dehors de la méthode de réception et est réutilisé à partir de threads multiples. En d'autres termes, ce n'est pas un fil sûr.

Rédiger un bon code de socket est difficile. Comme vous écrivez à la fois client et serveur, vous voudrez peut-être consulter Windows Fondation de communication (WCF) qui vous permet d'envoyer et de recevoir des objets entiers avec beaucoup moins de tracas.


1 commentaires

Merci d'avoir répondu. J'ai oublié de mentionner que les deux méthodes sont dans un bloc de verrouillage. Je vais mettre à jour le code de manière appropriée afin que d'autres puissent voir si je le verrouille même correctement.



3
votes

Je soupçonne que vous supposez que tout le message est livré dans un appel à reçus . Ce n'est pas, en général, le cas. La fragmentation, les retards, etc. ne peuvent finir par mieux que les données arrivent dans des dribbles, de sorte que vous obtiendrez votre fonction reçus appelé quand il n'y a que, par exemple. 900 octets prêts. Votre appel sock.receive (tampon, n, socketflags.none); dit "Donnez-moi les données dans le tampon, jusqu'à N octets" - Vous pouvez recevoir moins, et le nombre d'octets réellement lus est renvoyé par recevoir .

Ceci explique pourquoi la diminution du B , en ajoutant plus de retard ou en utilisant le même hôte semble "corriger" le problème - les chances sont considérablement augmentées que l'ensemble du message arrivera en une seule fois à l'aide de ces méthodes ( plus petit B , des données plus petites; l'ajout d'un moyen de retard signifie qu'il y a moins de données globales dans le tuyau; il n'y a pas de réseau dans la voie de l'adresse locale).

Pour voir s'il s'agit bien du problème, logez la valeur de retour de recevoir . Il sera occasionnellement moins de 1024 si mon diagnostic est correct. Pour résoudre ce problème, vous devez soit attendre que la mémoire tampon de la pile de réseau local ait toutes vos données (non idéales) ou simplement recevoir des données comme et lorsqu'elle arrive, le stockant localement jusqu'à ce qu'un message complet soit prêt et le traitement.


4 commentaires

C'est un problème que j'avais à un moment donné. Essayez de lire les données du réseau dans une boucle de temps jusqu'à ce que vous lisiez la quantité de données spécifiée dans le paramètre "longueur"


Reorging le 1024; Cela pourrait bien sûr échouer juste lors de la lecture de l'en-tête 2 octets


@MARC - Je veux dire que lors de la journalisation de la valeur de retour de la réception, vous verrez moins de 1024 dans le cas de test donné si le réseau sous-jacent fournit moins que tout le message en une réception.


Cela corrigé ça! Je viens de bloquer le code de réception dans une boucle de While qui attendit toutes les données. Je ne peux pas croire que je ne réalisais pas ça! Merci beaucoup!



1
votes

Lorsque vous utilisez des sockets, vous devez anticiper que la prise pourrait transférer moins d'octets que vous attendez. Vous devez faire boucle sur la méthode .Receive pour obtenir le reste des octets.

Ceci est également vrai lorsque vous envoyez des octets via une prise. Vous devez vérifier le nombre d'octets envoyés et la boucle sur l'envoi jusqu'à ce que tous les octets aient été envoyés.

Ce comportement est dû aux couches de réseau scintillant les messages en plusieurs paquets. Si vos messages sont courts, vous êtes moins susceptible de la rencontrer. Mais vous devriez toujours y coder.

Avec plus d'octets par tampon, vous êtes très susceptible de voir le message de l'expéditeur Spit dans plusieurs paquets. Chaque opération de lecture recevra un paquet, qui ne fait qu'une partie de votre message. Mais de petits tampons peuvent également être divisés.


0 commentaires

2
votes

Cela me semble que le problème principal ici est ne pas vérifier le résultat de Endreceive qui est le nombre d'octets lus. Si la prise est ouverte, cela peut être n'importe quoi > 0 et <= le max que vous avez spécifié. Il n'est pas sûr de supposer que parce que vous avez demandé 2 octets, 2 octets étaient lus. Idem lors de la lecture des données réelles.

Vous devez toujours boucler, accumuler la quantité de données que vous attendez (et diminuer le nombre restant et augmenter le décalage, en conséquence).

Le mélange de synchronisation / async ne vous aidera pas à faciliter la tâche, soit: £


0 commentaires

2
votes
sock.Receive(buffer, n, SocketFlags.None);
You not checking the return value of the function.  The socket will return up to 'n' bytes but will only return what is available.  Chance are you are not receiving the entirety of the 'data' with this read.  Thus, when you perform the next socket read, you are actually getting the first two bytes of the remainder of the data and not the length of the next transmission.

0 commentaires