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 sur 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. recv() se passe-t-il si recv() reçoit un paquet plus gros que mon tampon?
  3. Comment puis-je savoir si j’ai reçu l’intégralité du message sans appeler à nouveau recv et le faire attendre pour toujours s’il n’y a rien à recevoir?
  4. Y a-t-il un moyen de faire en sorte qu’un tampon n’ait pas un espace fixe, de sorte que je puisse continuer à y append sans crainte de manquer d’espace? peut-être utiliser strcat pour concaténer la dernière réponse de recv() au tampon?

Je sais qu’il y a beaucoup de questions en un, mais j’apprécierais beaucoup les réponses.

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

Comment savez-vous quelle taille faire passer le tampon à recv() ?

  • SOCK_STREAM : Ça n’a pas vraiment d’importance. Si votre protocole est un protocole transactionnel / interactif, choisissez simplement une taille pouvant contenir le message / la commande individuelle le plus important auquel vous pouvez raisonnablement vous attendre (3000 est probablement correct). Si votre protocole transfère des données en bloc, les tampons plus volumineux peuvent être plus efficaces – une bonne règle de base est à peu près la même que la taille de mémoire tampon du socket (souvent autour de 256 Ko).

  • SOCK_DGRAM : utilisez un tampon suffisamment grand pour contenir le plus gros paquet que votre protocole au niveau de l’application envoie. Si vous utilisez UDP, en général, votre protocole au niveau de l’application ne doit pas envoyer des paquets supérieurs à environ 1400 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 stream n’ont pas de concept de paquets – ce n’est qu’un stream continu d’octets. S’il y a plus d’octets à lire que votre tampon ne le permet, ils seront mis en queue par le système d’exploitation et disponibles pour votre prochain appel à recv .

  • SOCK_DGRAM : les octets en excès sont ignorés.

Comment savoir si j’ai reçu le message entier?

  • SOCK_STREAM : Vous devez créer 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 (en commençant chaque message par la longueur du message) ou d’un délimiteur de fin de message (qui pourrait être une nouvelle ligne dans un protocole textuel, par exemple). Une troisième option, moins utilisée, consiste à définir une taille fixe pour chaque message. Des combinaisons de ces options sont également possibles – par exemple, un en-tête de taille fixe incluant une valeur de longueur.

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

Y a-t-il un moyen de faire en sorte qu’un tampon n’ait pas un espace fixe, de sorte que je puisse continuer à y append sans craindre de manquer d’espace?

Cependant, vous pouvez essayer de redimensionner le tampon à l’aide de realloc() (s’il a été initialement alloué avec malloc() ou calloc() , c’est-à-dire).

Pour les protocoles de streaming tels que TCP, vous pouvez à peu près définir votre tampon à n’importe quelle taille. Cela dit, les 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 enregistré dans le kernel pour votre prochain appel à recv .

Oui, vous pouvez continuer à développer votre tampon. Vous pouvez faire un recv au milieu du tampon en commençant par offset idx , vous feriez:

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

Si vous possédez un socket SOCK_STREAM , recv obtient simplement “jusqu’à 3000 premiers octets” du stream. Il n’y a pas d’indications claires sur la taille de la mémoire tampon: la seule fois où vous connaissez la taille d’un stream, c’est quand tout est terminé ;-).

Si vous avez un socket SOCK_DGRAM et que le datagramme est plus grand que le tampon, recv remplit le tampon avec la première partie du datagramme, renvoie -1 et définit errno avec EMSGSIZE. Malheureusement, si le protocole est UDP, cela signifie que le rest 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 datagrammes fiables mais ils ne sont pas très populaires – je ne pouvais pas nommez-en un dans la famille TCP / IP, bien que vous connaissiez assez bien ce dernier ;-).

Pour développer un tampon dynamicment, allouez-le initialement avec malloc et utilisez realloc si nécessaire. Mais cela ne va pas vous aider avec une source UDP, hélas.

Pour le socket SOCK_STREAM , la taille du tampon n’a pas vraiment d’importance, car vous ne faites qu’extraire certains des octets en attente et vous pouvez en récupérer davantage lors d’un prochain appel. Choisissez simplement la taille de tampon que vous pouvez vous permettre.

Pour le socket SOCK_DGRAM , vous obtiendrez la partie adaptée du message en attente et le rest sera supprimé. Vous pouvez obtenir la taille de datagramme en attente avec l’ioctl suivant:

 #include  int size; ioctl(sockfd, FIONREAD, &size); 

Sinon, vous pouvez utiliser les 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); 

Ensuite, vous pouvez simplement faire un malloc() le tampon et récupérer le datagramme.

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

Selon la RFC 768 , la taille des paquets (en-tête inclus) pour UDP peut aller de 8 à 65 515 octets. Donc, la taille à l’épreuve des défaillances pour le tampon entrant sera de 65 507 octets (~ 64 Ko)

Cependant, tous les grands 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 Safe sur Internet?

16kb est à peu près juste; Si vous utilisez Ethernet Gigabit, chaque paquet peut avoir une taille de 9 Ko.