142 votes

Quelle doit être la taille de mon tampon recv lors de l'appel à recv dans la bibliothèque de sockets ?

J'ai quelques questions sur la bibliothèque socket en C. Voici un extrait de code auquel je me référerai dans mes questions.

char recv_buffer[3000];
recv(socket, recv_buffer, 3000, 0);
  1. Comment décider de la taille de recv_buffer ? J'utilise 3000, mais c'est arbitraire.
  2. que se passe-t-il si recv() reçoit un paquet plus grand que ma mémoire tampon ?
  3. Comment puis-je savoir si j'ai reçu l'intégralité du message sans appeler recv à nouveau et le faire attendre indéfiniment alors qu'il n'y a rien à recevoir ?
  4. Existe-t-il un moyen de faire en sorte qu'un tampon n'ait pas une quantité fixe d'espace, de sorte que je puisse continuer à l'alimenter sans craindre de manquer d'espace ? strcat pour concaténer les derniers recv() la réponse au tampon ?

Je sais que cela fait beaucoup de questions en une seule, mais j'apprécierais beaucoup les réponses.

241voto

caf Points 114951

Les réponses à ces questions varient selon que vous utilisez une socket stream ( SOCK_STREAM ) ou une socket de datagramme ( SOCK_DGRAM ) - dans le cadre de TCP/IP, le premier correspond à TCP et le second à UDP.

Comment savoir quelle taille donner à la mémoire tampon transmise à recv() ?

  • SOCK_STREAM : Cela n'a pas beaucoup d'importance. Si votre protocole est transactionnel / interactif, choisissez simplement une taille qui peut contenir le plus grand message individuel / commande que vous pouvez raisonnablement attendre (3000 est probablement suffisant). Si votre protocole transfère des données en masse, des tampons plus grands peuvent être plus efficaces - une bonne règle de base est de choisir une taille égale à la taille du tampon de réception du noyau de la socket (souvent quelque chose comme 256kB).

  • SOCK_DGRAM : Utilisez un tampon suffisamment grand pour contenir le plus gros paquet jamais envoyé par votre protocole au niveau de l'application. Si vous utilisez UDP, votre protocole d'application ne devrait généralement pas envoyer de paquets de plus de 1400 octets, car ils devront certainement être fragmentés et réassemblés.

Que se passe-t-il si recv reçoit un paquet plus grand que la mémoire tampon ?

  • SOCK_STREAM : La question n'a pas vraiment de sens telle qu'elle est posée, parce que les sockets stream n'ont pas de concept de paquets - ils sont juste un flux continu d'octets. S'il y a plus d'octets à lire que votre tampon n'en contient, ils seront mis en file d'attente par le système d'exploitation et disponibles pour votre prochain appel à recv .

  • SOCK_DGRAM : Les octets excédentaires sont rejetés.

Comment puis-je savoir si j'ai reçu l'intégralité du message ?

  • SOCK_STREAM : Vous devez intégrer un moyen de déterminer la fin du message dans votre protocole au niveau de l'application. Il s'agit généralement d'un préfixe de longueur (commençant chaque message par la longueur du message) ou d'un délimiteur de fin de message (qui peut être un simple retour à la ligne dans un protocole basé sur du texte, par exemple). Une troisième option, moins utilisée, consiste à imposer une taille fixe pour chaque message. Des combinaisons de ces options sont également possibles - par exemple, un en-tête de taille fixe qui inclut une valeur de longueur.

  • SOCK_DGRAM : Un seul recv renvoie toujours un seul datagramme.

Existe-t-il un moyen de faire en sorte qu'une mémoire tampon ne dispose pas d'une quantité fixe d'espace, de sorte que je puisse continuer à l'alimenter sans craindre de manquer d'espace ?

Non. Cependant, vous pouvez essayer de redimensionner la mémoire tampon en utilisant la fonction realloc() (s'il a été alloué à l'origine avec malloc() o calloc() c'est-à-dire).

1 votes

J'ai un "/r/n/r/n" à la fin d'un message dans le protocole que j'utilise. Et j'ai une boucle do while, à l'intérieur de laquelle j'appelle recv, je place le message au début de recv_buffer. et mon instruction while ressemble à ceci while( !(strstr(recv_buffer, " \r\n\r\n ")) ; Ma question est la suivante : est-il possible qu'un recv obtienne " \r\n "et dans le récapitulatif suivant, obtenir " \r\n afin que ma condition "while" ne se réalise jamais ?

3 votes

Oui, c'est le cas. Vous pouvez résoudre ce problème en faisant une boucle si vous n'avez pas de message complet et en insérant les octets du message suivant. recv dans le tampon qui suit le message partiel. Vous ne devez pas utiliser strstr() sur le tampon brut rempli par recv() - il n'y a aucune garantie qu'il contienne un terminateur nul, et il peut donc causer des problèmes de sécurité. strstr() pour s'écraser.

3 votes

Dans le cas de l'UDP, il n'y a rien de mal à envoyer des paquets UDP de plus de 1400 octets. La fragmentation est parfaitement légale et constitue un élément fondamental du protocole IP (même en IPv6, bien que l'expéditeur initial doive toujours procéder à la fragmentation). Pour UDP, vous êtes toujours sauvé si vous utilisez un tampon de 64 KB, car aucun paquet IP (v4 ou v6) ne peut avoir une taille supérieure à 64 KB (même lorsqu'il est fragmenté) et cela inclut même les en-têtes IIRC, de sorte que les données seront toujours inférieures à 64 KB à coup sûr.

18voto

R Samuel Klatchko Points 44549

Pour les protocoles de diffusion en continu tels que TCP, vous pouvez pratiquement fixer la taille de votre tampon à n'importe quelle valeur. Cela dit, il est recommandé d'utiliser des valeurs courantes qui sont des puissances de 2, telles que 4096 ou 8192.

S'il y a plus de données que ce que contient votre tampon, elles seront simplement sauvegardées dans le noyau pour votre prochain appel à recv .

Oui, vous pouvez continuer à augmenter votre tampon. Vous pouvez effectuer un recv au milieu de la mémoire tampon à partir de l'offset idx Vous le feriez :

recv(socket, recv_buffer + idx, recv_buffer_size - idx, 0);

7 votes

La puissance deux peut être plus efficace à plusieurs égards et est fortement suggérée.

3 votes

En complément de @theatrus, une efficacité notable est que l'opérateur modulo peut être remplacé par l'opérateur bitwise et avec un masque (par exemple x % 1024 == x & 1023), et la division entière peut être remplacée par une opération de décalage vers la droite (par exemple x / 1024 == x / 2^10 == x >> 10).

15voto

Alex Martelli Points 330805

Si vous avez un SOCK_STREAM de la prise de courant, recv récupère simplement "jusqu'aux 3000 premiers octets" du flux. Il n'y a pas d'indication claire sur la taille de la mémoire tampon : le seul moment où l'on sait quelle est la taille d'un flux, c'est lorsqu'il est terminé;-).

Si vous avez un SOCK_DGRAM et le datagramme est plus grand que le tampon, recv remplit le tampon avec la première partie du datagramme, renvoie -1, et met errno à EMSGSIZE. Malheureusement, si le protocole est UDP, cela signifie que le reste du datagramme est perdu -- c'est en partie pour cette raison qu'UDP est appelé un protocole peu fiable (je sais qu'il existe des protocoles de datagramme fiables, mais ils ne sont pas très populaires - je ne pourrais pas en citer un dans la famille TCP/IP, bien que je la connaisse assez bien;-).

Pour augmenter la taille d'une mémoire tampon de manière dynamique, allouez-la initialement à l'aide de la fonction malloc et utiliser realloc si nécessaire. Mais cela ne vous aidera pas à recv d'une source UDP, hélas.

7 votes

Comme UDP renvoie toujours au maximum un paquet UDP (même s'il y en a plusieurs dans le tampon de la socket) et qu'aucun paquet UDP ne peut dépasser 64 KB (un paquet IP peut au maximum faire 64 KB, même s'il est fragmenté), l'utilisation d'un tampon de 64 KB est absolument sûre et garantit que vous ne perdrez jamais de données lors d'une recv sur une socket UDP.

1voto

YeenFei Points 2100

Il n'y a pas de réponse absolue à votre question, car la technologie est toujours liée à une mise en œuvre spécifique. Je suppose que vous communiquez en UDP, car la taille de la mémoire tampon entrante ne pose pas de problème pour la communication TCP.

Selon le RFC 768 La taille des paquets (en-tête compris) pour UDP peut varier de 8 à 65 515 octets. La taille à toute épreuve du tampon entrant est donc de 65 507 octets (~64 Ko).

Cependant, tous les gros paquets ne peuvent pas être correctement acheminés par les périphériques du réseau, voir la discussion existante pour plus d'informations :

Quelle est la taille optimale d'un paquet UDP pour un débit maximal ?
Quelle est la plus grande taille de paquet UDP sûre sur Internet ?

-4voto

Andrew McGregor Points 7641

16kb est à peu près correct ; si vous utilisez un réseau Ethernet gigabit, chaque paquet pourrait avoir une taille de 9kb.

3 votes

Les sockets TCP sont des flux, ce qui signifie qu'un recv peut renvoyer des données accumulées à partir de plusieurs paquets, de sorte que la taille du paquet n'est absolument pas pertinente pour TCP. Dans le cas d'UDP, chaque appel recv renvoie au maximum un seul paquet UDP, la taille du paquet est donc importante, mais la taille correcte du paquet est d'environ 64 Ko, car un paquet UDP peut être (et sera souvent) fragmenté si nécessaire. Cependant, aucun paquet IP ne peut dépasser 64 Ko, même avec la fragmentation, et donc recv sur une socket UDP peut au maximum renvoyer 64 Ko (et ce qui n'est pas renvoyé est écarté pour le paquet en cours !)

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