web-dev-qa-db-fra.com

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

J'ai quelques questions à propos de la bibliothèque de sockets en C. Voici un extrait de code auquel je ferai référence 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 mon tampon?
  3. comment puis-je savoir si j'ai reçu l'intégralité du message sans appeler recv et le faire attendre indéfiniment lorsqu'il n'y a rien à recevoir?
  4. y a-t-il un moyen de faire en sorte qu'un tampon ne dispose pas d'une quantité d'espace fixe, afin de pouvoir continuer à l'ajouter sans craindre de manquer d'espace? peut-être en utilisant strcat pour concaténer la dernière réponse recv() au tampon?

Je sais qu'il y a beaucoup de questions dans une, mais j'apprécierais beaucoup vos réponses.

124
adhanlon

Les réponses à ces questions varient selon que vous utilisez un socket de flux (SOCK_STREAM) Ou un socket de datagramme (SOCK_DGRAM) - dans TCP/IP, l’ancien correspond à TCP et le dernier à UDP.

Comment savez-vous quelle est la taille du tampon passé à recv()?

  • SOCK_STREAM: Cela n'a pas vraiment d'importance. Si votre protocole est un protocole transactionnel/interactif, il vous suffit de choisir une taille pouvant contenir le plus grand message/commande individuel auquel vous pouvez raisonnablement vous attendre (3 000 correspond probablement à une amende). Si votre protocole transfère des données en vrac, des mémoires tampons plus grandes peuvent être plus efficaces - une bonne règle empirique est la même que la taille de la mémoire tampon de réception du socket (souvent environ 256 Ko).

  • 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 au niveau de l'application ne devrait généralement pas envoyer de paquets d'une taille supérieure à environ 1 400 octets, car ils devront certainement être fragmentés et réassemblés.

Que se passe-t-il si recv obtient un paquet plus grand que le tampon?

  • SOCK_STREAM: La question n'a pas vraiment de sens, car les sockets de flux n'ont pas de concept de paquets, mais simplement un flux d'octets continu. S'il y a plus d'octets disponibles à lire que votre tampon n'a plus de place, ils seront alors mis en file d'attente par le système d'exploitation et disponibles pour votre prochain appel à recv.

  • SOCK_DGRAM: Les octets en excès sont supprimés.

Comment puis-je savoir si j'ai reçu tout le message?

  • SOCK_STREAM: Vous devez créer un moyen de déterminer la fin du message dans votre protocole au niveau de l'application. Généralement, il s'agit soit d'un préfixe de longueur (commençant chaque message par la longueur du message), soit d'un délimiteur de fin de message (pouvant par exemple être une nouvelle ligne dans un protocole textuel). Une troisième option, moins utilisée, consiste à imposer une taille fixe à 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 appel recv renvoie toujours un seul datagramme.

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

Non. Cependant, vous pouvez essayer de redimensionner le tampon en utilisant realloc() (s'il avait été initialement alloué avec malloc() ou calloc()).

221
caf

Pour les protocoles de transmission en continu tels que TCP, vous pouvez configurer votre tampon à n’importe quelle taille. Cela dit, des valeurs communes qui sont des puissances de 2 telles que 4096 ou 8192 sont recommandées.

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

Oui, vous pouvez continuer à augmenter votre tampon. Vous pouvez faire une recv dans le milieu de la mémoire tampon en commençant à offset idx, vous feriez:

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

Si tu as un SOCK_STREAMsocket, recv ne récupère que les "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ù vous connaissez la taille d'un flux, c'est quand tout est terminé ;-).

Si tu as un SOCK_DGRAM socket, et le datagramme est plus grand que le tampon, recv remplit le tampon avec la première partie du datagramme, retourne -1 et initialise errno à EMSGSIZE. Malheureusement, si le protocole est UDP, cela signifie que le reste du datagramme est perdu - une partie de la raison pour laquelle UDP est appelé un protocole non fiable (je sais qu'il existe des protocoles de datagramme fiables mais ils ne sont pas très populaire - je ne saurais en nommer un dans la famille TCP/IP, même si je le connais assez bien ;-).

Pour développer un tampon de manière dynamique, allouez-le initialement avec malloc et utilisez realloc si nécessaire. Mais cela ne vous aidera pas avec recv d'une source UDP, hélas.

15
Alex Martelli

Pour la socket SOCK_STREAM, La taille de la mémoire tampon n'a pas vraiment d'importance, car vous extrayez simplement quelques octets en attente et vous pouvez en récupérer davantage lors d'un prochain appel. Il suffit de choisir la taille de la mémoire tampon que vous pouvez vous permettre.

Pour le socket SOCK_DGRAM, Vous obtiendrez la partie appropriée du message en attente et le reste sera ignoré. Vous pouvez obtenir la taille du datagramme en attente avec l'ioctl suivant:

#include <sys/ioctl.h>
int size;
ioctl(sockfd, FIONREAD, &size);

Vous pouvez également utiliser les indicateurs MSG_PEEK Et MSG_TRUNC De l'appel recv() pour obtenir la taille du datagramme en attente.

ssize_t size = recv(sockfd, buf, len, MSG_PEEK | MSG_TRUNC);

Vous avez besoin de MSG_PEEK Pour regarder (ne pas recevoir) le message en attente - recv renvoie la taille réelle et non tronquée; et vous avez besoin de MSG_TRUNC pour ne pas déborder de votre mémoire tampon actuelle.

Ensuite, vous pouvez simplement malloc(size) le tampon réel et recv() datagramme.

4
smokku

Il n’ya pas de réponse absolue à votre question, car la technologie doit toujours être spécifique à la mise en oeuvre. Je suppose que vous communiquez en UDP car la taille de la mémoire tampon entrante n’apporte pas de problème pour la communication TCP.

Selon RFC 768 , la taille du paquet (y compris l'en-tête) pour UDP peut aller de 8 à 65 515 octets. La taille de la mémoire tampon entrante 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 réseau. Reportez-vous à 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écurisé sur Internet

1
YeenFei