2 votes

c# socket multiple packets stack

J'ai un problème avec l'utilisation de Sockets en c#. Voici un exemple. Disons que j'envoie le numéro 1, puis j'envoie immédiatement le numéro 2. Le problème que je rencontre parfois est que le client qui est censé le recevoir recevra un paquet contenant '12'. Je me demandais s'il existait un moyen intégré de distinguer les paquets sans utiliser de caractères ou autre pour séparer les données.

Pour résumer, j'ai parlé de deux paquets. Un avec le numéro "1", un avec le numéro "2". Le serveur reçoit un paquet avec la donnée '12'. Je ne veux pas séparer les paquets par des caractères, comme ceci ':1::2:' ou autre, car je n'ai pas toujours le contrôle sur le format des données entrantes.

Des idées ?

Par exemple, si je fais ceci

client.Send(new byte[1]{'1'}, 1,SocketFlags.None);
client.Send(new byte[1]{'2'}, 1,SocketFlags.None);

puis, du côté du serveur

byte[] data = new byte[1024];
client.Receive(data);

data revient parfois avec "12" même si je fais deux envois séparés.

6voto

Stephen Cleary Points 91731

TCP/IP fonctionne sur un flux l'abstraction, no a paquet l'abstraction.

Lorsque vous appelez Send , vous êtes no l'envoi d'un paquet. Vous ne faites qu'ajouter des octets à un flux.

Lorsque vous appelez Receive , vous êtes no réception d'un paquet. Vous ne recevez que des octets d'un flux.

Ainsi, vous doit définir où commence et où finit un "message". Il n'y a que trois solutions possibles :

  1. Chaque message a une taille connue (par exemple, tous les messages ont une taille de 42 octets). Dans ce cas, il suffit de conserver Receive Les données doivent être enregistrées jusqu'à ce que l'on obtienne ce nombre d'octets.
  2. Utilisez des délimiteurs, que vous connaissez déjà.
  3. Utiliser le préfixage de longueur (par exemple, préfixer chaque message avec une longueur de message de 4 octets). Dans ce cas, vous conservez Receive Le message est ensuite décodé pour obtenir la longueur réelle du message, puis il est conservé dans la mémoire de l'utilisateur. Receive le message lui-même.

Vous devez encadrer vous-même le message. TCP/IP ne peut pas le faire à votre place.

5voto

meklarian Points 4915

Le protocole TCP est un protocole de diffusion en continu, vous recevrez donc toujours les données arrivées depuis la dernière lecture, jusqu'à la taille de la fenêtre de diffusion en continu du côté du destinataire. Ce tampon peut être rempli de données reçues à partir de plusieurs paquets de n'importe quelle taille envoyés par l'expéditeur.

Taille de la fenêtre de réception TCP et mise à l'échelle @ MSDN

Bien que vous ayez observé dans votre exemple un blob unifié de données contenant 2 octets à la réception, il est possible de recevoir des séquences de 1 octet par 1 octet (telles qu'envoyées par votre expéditeur) en fonction des conditions du réseau, ainsi que de nombreuses combinaisons possibles de lectures de 0, 1 et 2 octets si vous effectuez des lectures non bloquantes. Lors du débogage sur un réseau local typique non congestionné ou sur une configuration de bouclage, vous ne verrez presque jamais cela s'il n'y a pas de retard du côté de l'envoi. Il existe des moyens, à des niveaux inférieurs de la pile réseau, de détecter la transmission par paquet, mais ils ne sont pas utilisés dans la programmation TCP typique et sont hors de portée de l'application.

Si vous passez à UDP, chaque paquet sera reçu tel qu'il a été envoyé, ce qui correspond à vos attentes. Cela peut vous convenir davantage, mais gardez à l'esprit qu'UDP ne fait aucune promesse de livraison et que le routage du réseau peut faire en sorte que les paquets soient livrés dans le désordre.

Vous devriez envisager de délimiter vos données ou de trouver une autre méthode pour détecter la fin d'une unité de données telle que définie par votre application, et vous en tenir à TCP.

2voto

Polity Points 7316

Il est difficile de répondre à votre question sans connaître le contexte. En revanche, par défaut, TCP/IP gère automatiquement les paquets pour vous (bien que vous les receviez par flux). Cependant, lorsque vous avez une implémentation très spécifique (mauvaise ?), vous pouvez envoyer plusieurs flux sur une socket en même temps, rendant impossible pour TCP/IP de niveau inférieur de détecter la différence. Il est donc très difficile pour vous d'identifier les différents flux sur le client. La seule solution serait d'envoyer deux flux totalement uniques (par exemple, le flux 1 n'envoie que des octets inférieurs à 127 et le flux 2 n'envoie que des octets supérieurs ou égaux à 127). Encore une fois, il s'agit d'un comportement épouvantable

1voto

aaaa bbbb Points 1487

Vous devez insérer des délimiteurs dans vos messages TCP ou utiliser le comptage des octets pour savoir où commence un message et où commence un autre.

Votre code comporte une autre erreur grave. Les sockets TCP peuvent ne pas vous fournir toutes les données en un seul appel à Receive. Vous devez continuer à recevoir des données en boucle jusqu'à ce que les enregistrements spécifiques à votre application dans le flux de données indiquent que le message entier a été reçu. Votre appel à client.Receive(data) renvoie le nombre d'octets reçus. Vous devez capturer ce nombre.

De même, lorsque vous envoyez des données, il se peut que toutes les données ne soient pas envoyées en un seul appel. Vous devez boucler l'envoi de données jusqu'à ce que le nombre d'octets envoyés soit égal à ce que vous aviez l'intention d'envoyer. L'appel à client.Send renvoie le nombre réel d'octets envoyés, qui peut ne pas correspondre à la totalité de ce que vous avez essayé d'envoyer !

L'erreur la plus commune que je vois les gens faire avec les sockets est qu'ils ne font pas de boucle sur l'envoi et la réception. Si vous comprenez pourquoi vous avez besoin de boucler, alors vous savez pourquoi vous avez besoin d'un délimiteur ou d'un nombre d'octets.

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